mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-11 22:16:56 +03:00
fixed rebase conflicts
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,5 +1,5 @@
|
||||
[submodule "code/components/esp32-camera-master"]
|
||||
path = code/components/esp32-camera-master
|
||||
[submodule "code/components/esp32-camera"]
|
||||
path = code/components/esp32-camera
|
||||
url = https://github.com/espressif/esp32-camera.git
|
||||
[submodule "code/components/esp-nn"]
|
||||
path = code/components/esp-nn
|
||||
|
||||
1
code/components/esp32-camera
Submodule
1
code/components/esp32-camera
Submodule
Submodule code/components/esp32-camera added at de025b8f40
@@ -4,6 +4,6 @@ list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/proto
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp32-camera-master esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||
REQUIRES esp32-camera esp_http_server jomjol_logfile jomjol_image_proc nvs_flash jomjol_fileserver_ota jomjol_controlGPIO)
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES jomjol_image_proc jomjol_logfile esp_http_server esp32-camera-master jomjol_controlcamera jomjol_flowcontroll jomjol_helper)
|
||||
REQUIRES jomjol_image_proc jomjol_logfile esp_http_server esp32-camera jomjol_controlcamera jomjol_flowcontroll jomjol_helper)
|
||||
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
## TODO: GLOB is not a good way to collect files. Use explicit file list instead
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(tflite_dir "${CMAKE_CURRENT_SOURCE_DIR}/tensorflow/lite")
|
||||
set(tfmicro_dir "${tflite_dir}/micro")
|
||||
set(tfmicro_frontend_dir "${tflite_dir}/experimental/microfrontend/lib")
|
||||
set(tfmicro_kernels_dir "${tfmicro_dir}/kernels")
|
||||
|
||||
file(GLOB srcs_micro
|
||||
"${tfmicro_dir}/*.cc"
|
||||
"${tfmicro_dir}/*.c")
|
||||
|
||||
file(GLOB src_micro_frontend
|
||||
"${tfmicro_frontend_dir}/*.c"
|
||||
"${tfmicro_frontend_dir}/*.cc")
|
||||
file(GLOB srcs_kernels
|
||||
"${tfmicro_kernels_dir}/*.c"
|
||||
"${tfmicro_kernels_dir}/*.cc")
|
||||
|
||||
# remove sources which will be provided by esp_nn
|
||||
list(REMOVE_ITEM srcs_kernels
|
||||
"${tfmicro_kernels_dir}/add.cc"
|
||||
"${tfmicro_kernels_dir}/conv.cc"
|
||||
"${tfmicro_kernels_dir}/depthwise_conv.cc"
|
||||
"${tfmicro_kernels_dir}/fully_connected.cc"
|
||||
"${tfmicro_kernels_dir}/mul.cc"
|
||||
"${tfmicro_kernels_dir}/pooling.cc"
|
||||
"${tfmicro_kernels_dir}/softmax.cc")
|
||||
|
||||
FILE(GLOB esp_nn_kernels
|
||||
"${tfmicro_kernels_dir}/esp_nn/*.cc")
|
||||
|
||||
set(lib_srcs
|
||||
"${srcs_micro}"
|
||||
"${srcs_kernels}"
|
||||
"${esp_nn_kernels}"
|
||||
"${src_micro_frontend}"
|
||||
"${tflite_dir}/kernels/kernel_util.cc"
|
||||
"${tflite_dir}/micro/memory_planner/greedy_memory_planner.cc"
|
||||
"${tflite_dir}/micro/memory_planner/linear_memory_planner.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/non_persistent_arena_buffer_allocator.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/persistent_arena_buffer_allocator.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/recording_single_arena_buffer_allocator.cc"
|
||||
"${tflite_dir}/micro/arena_allocator/single_arena_buffer_allocator.cc"
|
||||
"${tflite_dir}/c/common.cc"
|
||||
"${tflite_dir}/core/api/error_reporter.cc"
|
||||
"${tflite_dir}/core/api/flatbuffer_conversions.cc"
|
||||
"${tflite_dir}/core/api/op_resolver.cc"
|
||||
"${tflite_dir}/core/api/tensor_utils.cc"
|
||||
"${tflite_dir}/kernels/internal/quantization_util.cc"
|
||||
"${tflite_dir}/schema/schema_utils.cc")
|
||||
|
||||
set(priv_req esp-nn)
|
||||
|
||||
# include component requirements which were introduced after IDF version 4.1
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
|
||||
list(APPEND priv_req esp_timer driver)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS "${lib_srcs}"
|
||||
INCLUDE_DIRS "." "third_party/gemmlowp"
|
||||
"third_party/flatbuffers/include"
|
||||
"third_party/ruy"
|
||||
"third_party/kissfft"
|
||||
REQUIRES ${pub_req}
|
||||
PRIV_REQUIRES ${priv_req})
|
||||
|
||||
# Reduce the level of paranoia to be able to compile TF sources
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE
|
||||
-Wno-maybe-uninitialized
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-error=sign-compare
|
||||
-Wno-error=double-promotion
|
||||
-DESP_NN # enables ESP-NN optimizations by Espressif
|
||||
-Wno-type-limits)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -Wno-nonnull)
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE $<$<COMPILE_LANGUAGE:CXX>: -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -Wno-return-type -Wno-strict-aliasing -std=gnu++14 >)
|
||||
target_compile_options(${COMPONENT_LIB} INTERFACE $<$<IN_LIST:-DTF_LITE_STATIC_MEMORY,$<TARGET_PROPERTY:${COMPONENT_LIB},COMPILE_OPTIONS>>:-DTF_LITE_STATIC_MEMORY>)
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE -lm)
|
||||
@@ -1,194 +0,0 @@
|
||||
/* Copyright 2018 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
#define TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
|
||||
// DO NOT EDIT MANUALLY: This file is automatically generated by
|
||||
// `schema/builtin_ops_header/generator.cc`.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// The enum for builtin operators.
|
||||
// Note: CUSTOM, DELEGATE, and PLACEHOLDER_FOR_GREATER_OP_CODES are 3 special
|
||||
// ops which are not real built-in ops.
|
||||
typedef enum {
|
||||
kTfLiteBuiltinAdd = 0,
|
||||
kTfLiteBuiltinAveragePool2d = 1,
|
||||
kTfLiteBuiltinConcatenation = 2,
|
||||
kTfLiteBuiltinConv2d = 3,
|
||||
kTfLiteBuiltinDepthwiseConv2d = 4,
|
||||
kTfLiteBuiltinDepthToSpace = 5,
|
||||
kTfLiteBuiltinDequantize = 6,
|
||||
kTfLiteBuiltinEmbeddingLookup = 7,
|
||||
kTfLiteBuiltinFloor = 8,
|
||||
kTfLiteBuiltinFullyConnected = 9,
|
||||
kTfLiteBuiltinHashtableLookup = 10,
|
||||
kTfLiteBuiltinL2Normalization = 11,
|
||||
kTfLiteBuiltinL2Pool2d = 12,
|
||||
kTfLiteBuiltinLocalResponseNormalization = 13,
|
||||
kTfLiteBuiltinLogistic = 14,
|
||||
kTfLiteBuiltinLshProjection = 15,
|
||||
kTfLiteBuiltinLstm = 16,
|
||||
kTfLiteBuiltinMaxPool2d = 17,
|
||||
kTfLiteBuiltinMul = 18,
|
||||
kTfLiteBuiltinRelu = 19,
|
||||
kTfLiteBuiltinReluN1To1 = 20,
|
||||
kTfLiteBuiltinRelu6 = 21,
|
||||
kTfLiteBuiltinReshape = 22,
|
||||
kTfLiteBuiltinResizeBilinear = 23,
|
||||
kTfLiteBuiltinRnn = 24,
|
||||
kTfLiteBuiltinSoftmax = 25,
|
||||
kTfLiteBuiltinSpaceToDepth = 26,
|
||||
kTfLiteBuiltinSvdf = 27,
|
||||
kTfLiteBuiltinTanh = 28,
|
||||
kTfLiteBuiltinConcatEmbeddings = 29,
|
||||
kTfLiteBuiltinSkipGram = 30,
|
||||
kTfLiteBuiltinCall = 31,
|
||||
kTfLiteBuiltinCustom = 32,
|
||||
kTfLiteBuiltinEmbeddingLookupSparse = 33,
|
||||
kTfLiteBuiltinPad = 34,
|
||||
kTfLiteBuiltinUnidirectionalSequenceRnn = 35,
|
||||
kTfLiteBuiltinGather = 36,
|
||||
kTfLiteBuiltinBatchToSpaceNd = 37,
|
||||
kTfLiteBuiltinSpaceToBatchNd = 38,
|
||||
kTfLiteBuiltinTranspose = 39,
|
||||
kTfLiteBuiltinMean = 40,
|
||||
kTfLiteBuiltinSub = 41,
|
||||
kTfLiteBuiltinDiv = 42,
|
||||
kTfLiteBuiltinSqueeze = 43,
|
||||
kTfLiteBuiltinUnidirectionalSequenceLstm = 44,
|
||||
kTfLiteBuiltinStridedSlice = 45,
|
||||
kTfLiteBuiltinBidirectionalSequenceRnn = 46,
|
||||
kTfLiteBuiltinExp = 47,
|
||||
kTfLiteBuiltinTopkV2 = 48,
|
||||
kTfLiteBuiltinSplit = 49,
|
||||
kTfLiteBuiltinLogSoftmax = 50,
|
||||
kTfLiteBuiltinDelegate = 51,
|
||||
kTfLiteBuiltinBidirectionalSequenceLstm = 52,
|
||||
kTfLiteBuiltinCast = 53,
|
||||
kTfLiteBuiltinPrelu = 54,
|
||||
kTfLiteBuiltinMaximum = 55,
|
||||
kTfLiteBuiltinArgMax = 56,
|
||||
kTfLiteBuiltinMinimum = 57,
|
||||
kTfLiteBuiltinLess = 58,
|
||||
kTfLiteBuiltinNeg = 59,
|
||||
kTfLiteBuiltinPadv2 = 60,
|
||||
kTfLiteBuiltinGreater = 61,
|
||||
kTfLiteBuiltinGreaterEqual = 62,
|
||||
kTfLiteBuiltinLessEqual = 63,
|
||||
kTfLiteBuiltinSelect = 64,
|
||||
kTfLiteBuiltinSlice = 65,
|
||||
kTfLiteBuiltinSin = 66,
|
||||
kTfLiteBuiltinTransposeConv = 67,
|
||||
kTfLiteBuiltinSparseToDense = 68,
|
||||
kTfLiteBuiltinTile = 69,
|
||||
kTfLiteBuiltinExpandDims = 70,
|
||||
kTfLiteBuiltinEqual = 71,
|
||||
kTfLiteBuiltinNotEqual = 72,
|
||||
kTfLiteBuiltinLog = 73,
|
||||
kTfLiteBuiltinSum = 74,
|
||||
kTfLiteBuiltinSqrt = 75,
|
||||
kTfLiteBuiltinRsqrt = 76,
|
||||
kTfLiteBuiltinShape = 77,
|
||||
kTfLiteBuiltinPow = 78,
|
||||
kTfLiteBuiltinArgMin = 79,
|
||||
kTfLiteBuiltinFakeQuant = 80,
|
||||
kTfLiteBuiltinReduceProd = 81,
|
||||
kTfLiteBuiltinReduceMax = 82,
|
||||
kTfLiteBuiltinPack = 83,
|
||||
kTfLiteBuiltinLogicalOr = 84,
|
||||
kTfLiteBuiltinOneHot = 85,
|
||||
kTfLiteBuiltinLogicalAnd = 86,
|
||||
kTfLiteBuiltinLogicalNot = 87,
|
||||
kTfLiteBuiltinUnpack = 88,
|
||||
kTfLiteBuiltinReduceMin = 89,
|
||||
kTfLiteBuiltinFloorDiv = 90,
|
||||
kTfLiteBuiltinReduceAny = 91,
|
||||
kTfLiteBuiltinSquare = 92,
|
||||
kTfLiteBuiltinZerosLike = 93,
|
||||
kTfLiteBuiltinFill = 94,
|
||||
kTfLiteBuiltinFloorMod = 95,
|
||||
kTfLiteBuiltinRange = 96,
|
||||
kTfLiteBuiltinResizeNearestNeighbor = 97,
|
||||
kTfLiteBuiltinLeakyRelu = 98,
|
||||
kTfLiteBuiltinSquaredDifference = 99,
|
||||
kTfLiteBuiltinMirrorPad = 100,
|
||||
kTfLiteBuiltinAbs = 101,
|
||||
kTfLiteBuiltinSplitV = 102,
|
||||
kTfLiteBuiltinUnique = 103,
|
||||
kTfLiteBuiltinCeil = 104,
|
||||
kTfLiteBuiltinReverseV2 = 105,
|
||||
kTfLiteBuiltinAddN = 106,
|
||||
kTfLiteBuiltinGatherNd = 107,
|
||||
kTfLiteBuiltinCos = 108,
|
||||
kTfLiteBuiltinWhere = 109,
|
||||
kTfLiteBuiltinRank = 110,
|
||||
kTfLiteBuiltinElu = 111,
|
||||
kTfLiteBuiltinReverseSequence = 112,
|
||||
kTfLiteBuiltinMatrixDiag = 113,
|
||||
kTfLiteBuiltinQuantize = 114,
|
||||
kTfLiteBuiltinMatrixSetDiag = 115,
|
||||
kTfLiteBuiltinRound = 116,
|
||||
kTfLiteBuiltinHardSwish = 117,
|
||||
kTfLiteBuiltinIf = 118,
|
||||
kTfLiteBuiltinWhile = 119,
|
||||
kTfLiteBuiltinNonMaxSuppressionV4 = 120,
|
||||
kTfLiteBuiltinNonMaxSuppressionV5 = 121,
|
||||
kTfLiteBuiltinScatterNd = 122,
|
||||
kTfLiteBuiltinSelectV2 = 123,
|
||||
kTfLiteBuiltinDensify = 124,
|
||||
kTfLiteBuiltinSegmentSum = 125,
|
||||
kTfLiteBuiltinBatchMatmul = 126,
|
||||
kTfLiteBuiltinPlaceholderForGreaterOpCodes = 127,
|
||||
kTfLiteBuiltinCumsum = 128,
|
||||
kTfLiteBuiltinCallOnce = 129,
|
||||
kTfLiteBuiltinBroadcastTo = 130,
|
||||
kTfLiteBuiltinRfft2d = 131,
|
||||
kTfLiteBuiltinConv3d = 132,
|
||||
kTfLiteBuiltinImag = 133,
|
||||
kTfLiteBuiltinReal = 134,
|
||||
kTfLiteBuiltinComplexAbs = 135,
|
||||
kTfLiteBuiltinHashtable = 136,
|
||||
kTfLiteBuiltinHashtableFind = 137,
|
||||
kTfLiteBuiltinHashtableImport = 138,
|
||||
kTfLiteBuiltinHashtableSize = 139,
|
||||
kTfLiteBuiltinReduceAll = 140,
|
||||
kTfLiteBuiltinConv3dTranspose = 141,
|
||||
kTfLiteBuiltinVarHandle = 142,
|
||||
kTfLiteBuiltinReadVariable = 143,
|
||||
kTfLiteBuiltinAssignVariable = 144,
|
||||
kTfLiteBuiltinBroadcastArgs = 145,
|
||||
kTfLiteBuiltinRandomStandardNormal = 146,
|
||||
kTfLiteBuiltinBucketize = 147,
|
||||
kTfLiteBuiltinRandomUniform = 148,
|
||||
kTfLiteBuiltinMultinomial = 149,
|
||||
kTfLiteBuiltinGelu = 150,
|
||||
kTfLiteBuiltinDynamicUpdateSlice = 151,
|
||||
kTfLiteBuiltinRelu0To1 = 152,
|
||||
kTfLiteBuiltinUnsortedSegmentProd = 153,
|
||||
kTfLiteBuiltinUnsortedSegmentMax = 154,
|
||||
kTfLiteBuiltinUnsortedSegmentSum = 155,
|
||||
kTfLiteBuiltinAtan2 = 156,
|
||||
kTfLiteBuiltinUnsortedSegmentMin = 157,
|
||||
kTfLiteBuiltinSign = 158,
|
||||
} TfLiteBuiltinOperator;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif // TENSORFLOW_LITE_BUILTIN_OPS_H_
|
||||
@@ -1,307 +0,0 @@
|
||||
/* 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"
|
||||
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
#include "tensorflow/lite/tensorflow_profiler_logger.h"
|
||||
#endif
|
||||
|
||||
#ifndef TF_LITE_STATIC_MEMORY
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#endif // TF_LITE_STATIC_MEMORY
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t TfLiteIntArrayGetSizeInBytes(int size) {
|
||||
static TfLiteIntArray dummy;
|
||||
|
||||
size_t computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_size;
|
||||
}
|
||||
|
||||
int TfLiteIntArrayEqual(const TfLiteIntArray* a, const TfLiteIntArray* b) {
|
||||
if (a == b) return 1;
|
||||
if (a == nullptr || b == nullptr) return 0;
|
||||
return TfLiteIntArrayEqualsArray(a, b->size, b->data);
|
||||
}
|
||||
|
||||
int TfLiteIntArrayEqualsArray(const TfLiteIntArray* a, int b_size,
|
||||
const int b_data[]) {
|
||||
if (a == nullptr) 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) {
|
||||
size_t alloc_size = TfLiteIntArrayGetSizeInBytes(size);
|
||||
if (alloc_size <= 0) return nullptr;
|
||||
TfLiteIntArray* ret = (TfLiteIntArray*)malloc(alloc_size);
|
||||
if (!ret) return ret;
|
||||
ret->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TfLiteIntArray* TfLiteIntArrayCopy(const TfLiteIntArray* src) {
|
||||
if (!src) return nullptr;
|
||||
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;
|
||||
|
||||
int computed_size = sizeof(dummy) + sizeof(dummy.data[0]) * size;
|
||||
#if defined(_MSC_VER)
|
||||
// Context for why this is needed is in http://b/189926408#comment21
|
||||
computed_size -= sizeof(dummy.data[0]);
|
||||
#endif
|
||||
return computed_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) {
|
||||
if (t->data.raw) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(t);
|
||||
#endif
|
||||
free(t->data.raw);
|
||||
}
|
||||
}
|
||||
t->data.raw = nullptr;
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
}
|
||||
if (q_params->zero_point) {
|
||||
TfLiteIntArrayFree(q_params->zero_point);
|
||||
q_params->zero_point = nullptr;
|
||||
}
|
||||
free(q_params);
|
||||
}
|
||||
quantization->params = nullptr;
|
||||
quantization->type = kTfLiteNoQuantization;
|
||||
}
|
||||
|
||||
void TfLiteSparsityFree(TfLiteSparsity* sparsity) {
|
||||
if (sparsity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sparsity->traversal_order) {
|
||||
TfLiteIntArrayFree(sparsity->traversal_order);
|
||||
sparsity->traversal_order = nullptr;
|
||||
}
|
||||
|
||||
if (sparsity->block_map) {
|
||||
TfLiteIntArrayFree(sparsity->block_map);
|
||||
sparsity->block_map = nullptr;
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
TfLiteIntArrayFree(metadata.array_indices);
|
||||
metadata.array_indices = nullptr;
|
||||
}
|
||||
}
|
||||
free(sparsity->dim_metadata);
|
||||
sparsity->dim_metadata = nullptr;
|
||||
}
|
||||
|
||||
free(sparsity);
|
||||
}
|
||||
|
||||
void TfLiteTensorFree(TfLiteTensor* t) {
|
||||
TfLiteTensorDataFree(t);
|
||||
if (t->dims) TfLiteIntArrayFree(t->dims);
|
||||
t->dims = nullptr;
|
||||
|
||||
if (t->dims_signature) {
|
||||
TfLiteIntArrayFree((TfLiteIntArray*)t->dims_signature);
|
||||
}
|
||||
t->dims_signature = nullptr;
|
||||
|
||||
TfLiteQuantizationFree(&t->quantization);
|
||||
TfLiteSparsityFree(t->sparsity);
|
||||
t->sparsity = nullptr;
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
}
|
||||
|
||||
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst) {
|
||||
if (!src || !dst) return kTfLiteOk;
|
||||
if (src->bytes != dst->bytes) return kTfLiteError;
|
||||
if (src == dst) return kTfLiteOk;
|
||||
|
||||
dst->type = src->type;
|
||||
if (dst->dims) TfLiteIntArrayFree(dst->dims);
|
||||
dst->dims = TfLiteIntArrayCopy(src->dims);
|
||||
memcpy(dst->data.raw, src->data.raw, src->bytes);
|
||||
dst->buffer_handle = src->buffer_handle;
|
||||
dst->data_is_stale = src->data_is_stale;
|
||||
dst->delegate = src->delegate;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
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 = (char*)malloc(num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
} else if (num_bytes > tensor->bytes) {
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorDealloc(tensor);
|
||||
#endif
|
||||
tensor->data.raw = (char*)realloc(tensor->data.raw, num_bytes);
|
||||
#ifdef TF_LITE_TENSORFLOW_PROFILER
|
||||
tflite::OnTfLiteTensorAlloc(tensor, num_bytes);
|
||||
#endif
|
||||
}
|
||||
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 kTfLiteUInt16:
|
||||
return "UINT16";
|
||||
case kTfLiteInt16:
|
||||
return "INT16";
|
||||
case kTfLiteInt32:
|
||||
return "INT32";
|
||||
case kTfLiteUInt32:
|
||||
return "UINT32";
|
||||
case kTfLiteUInt8:
|
||||
return "UINT8";
|
||||
case kTfLiteInt8:
|
||||
return "INT8";
|
||||
case kTfLiteInt64:
|
||||
return "INT64";
|
||||
case kTfLiteUInt64:
|
||||
return "UINT64";
|
||||
case kTfLiteBool:
|
||||
return "BOOL";
|
||||
case kTfLiteComplex64:
|
||||
return "COMPLEX64";
|
||||
case kTfLiteComplex128:
|
||||
return "COMPLEX128";
|
||||
case kTfLiteString:
|
||||
return "STRING";
|
||||
case kTfLiteFloat16:
|
||||
return "FLOAT16";
|
||||
case kTfLiteFloat64:
|
||||
return "FLOAT64";
|
||||
case kTfLiteResource:
|
||||
return "RESOURCE";
|
||||
case kTfLiteVariant:
|
||||
return "VARIANT";
|
||||
}
|
||||
return "Unknown type";
|
||||
}
|
||||
|
||||
TfLiteDelegate TfLiteDelegateCreate() { return TfLiteDelegate{}; }
|
||||
|
||||
struct TfLiteOpaqueDelegateStruct* TfLiteOpaqueDelegateCreate(
|
||||
const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder) {
|
||||
if (!opaque_delegate_builder) return nullptr;
|
||||
|
||||
TfLiteDelegate* result = new TfLiteDelegate{};
|
||||
result->opaque_delegate_builder = new TfLiteOpaqueDelegateBuilder{};
|
||||
*(result->opaque_delegate_builder) = *opaque_delegate_builder;
|
||||
|
||||
return reinterpret_cast<struct TfLiteOpaqueDelegateStruct*>(result);
|
||||
}
|
||||
|
||||
void TfLiteOpaqueDelegateDelete(
|
||||
const struct TfLiteOpaqueDelegateStruct* opaque_delegate) {
|
||||
if (!opaque_delegate) return;
|
||||
|
||||
const TfLiteDelegate* tflite_delegate =
|
||||
reinterpret_cast<const TfLiteDelegate*>(opaque_delegate);
|
||||
delete tflite_delegate->opaque_delegate_builder;
|
||||
delete tflite_delegate;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
/* Copyright 2017 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.
|
||||
==============================================================================*/
|
||||
/// \file
|
||||
/// This provides a few C++ helpers that are useful for manipulating C
|
||||
/// structures in C++.
|
||||
#ifndef TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
#define TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
/// Provides a range iterable wrapper for TfLiteIntArray* (C lists) that TfLite
|
||||
/// C api uses.
|
||||
// Can't use the google array_view, since we can't depend on even
|
||||
// absl for embedded device reasons.
|
||||
class TfLiteIntArrayView {
|
||||
public:
|
||||
/// Construct a view of a TfLiteIntArray*. Note, `int_array` should be
|
||||
/// non-null and this view does not take ownership of it.
|
||||
explicit TfLiteIntArrayView(const TfLiteIntArray* int_array)
|
||||
: int_array_(int_array) {}
|
||||
|
||||
TfLiteIntArrayView(const TfLiteIntArrayView&) = default;
|
||||
TfLiteIntArrayView& operator=(const TfLiteIntArrayView& rhs) = default;
|
||||
|
||||
typedef const int* const_iterator;
|
||||
const_iterator begin() const { return int_array_->data; }
|
||||
const_iterator end() const { return &int_array_->data[int_array_->size]; }
|
||||
size_t size() const { return end() - begin(); }
|
||||
int operator[](size_t pos) const { return int_array_->data[pos]; }
|
||||
|
||||
private:
|
||||
const TfLiteIntArray* int_array_;
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CONTEXT_UTIL_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,412 +0,0 @@
|
||||
/* Copyright 2021 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
#define TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
|
||||
// These functions transform codes and data structures that are defined in the
|
||||
// flatbuffer serialization format into in-memory values that are used by the
|
||||
// runtime API and interpreter.
|
||||
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// Interface class for builtin data allocations.
|
||||
class BuiltinDataAllocator {
|
||||
public:
|
||||
virtual void* Allocate(size_t size, size_t alignment_hint) = 0;
|
||||
virtual void Deallocate(void* data) = 0;
|
||||
|
||||
// Allocate a structure, but make sure it is a POD structure that doesn't
|
||||
// require constructors to run. The reason we do this, is that Interpreter's C
|
||||
// extension part will take ownership so destructors will not be run during
|
||||
// deallocation.
|
||||
template <typename T>
|
||||
T* AllocatePOD() {
|
||||
// TODO(b/154346074): Change this to is_trivially_destructible when all
|
||||
// platform targets support that properly.
|
||||
static_assert(std::is_pod<T>::value, "Builtin data structure must be POD.");
|
||||
void* allocated_memory = this->Allocate(sizeof(T), alignof(T));
|
||||
return new (allocated_memory) T();
|
||||
}
|
||||
|
||||
virtual ~BuiltinDataAllocator() {}
|
||||
};
|
||||
|
||||
// Parse the appropriate data out of the op.
|
||||
//
|
||||
// This handles builtin data explicitly as there are flatbuffer schemas.
|
||||
// If it returns kTfLiteOk, it passes the data out with `builtin_data`. The
|
||||
// calling function has to pass in an allocator object, and this allocator
|
||||
// will be called to reserve space for the output data. If the calling
|
||||
// function's allocator reserves memory on the heap, then it's the calling
|
||||
// function's responsibility to free it.
|
||||
// If it returns kTfLiteError, `builtin_data` will be `nullptr`.
|
||||
TfLiteStatus ParseOpData(const Operator* op, BuiltinOperator op_type,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
// Converts the tensor data type used in the flat buffer to the representation
|
||||
// used by the runtime.
|
||||
TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType* type,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
TfLiteStatus ParseAbs(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAdd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAddN(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseArgMax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseArgMin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseAssignVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBatchMatMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBatchToSpaceNd(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBroadcastArgs(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseBroadcastTo(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCallOnce(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCeil(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCast(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseConcatenation(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseConv2D(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCos(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseCumsum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDepthToSpace(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDepthwiseConv2D(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDequantize(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseDiv(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseElu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseExp(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseExpandDims(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFill(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloor(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloorDiv(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFloorMod(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseFullyConnected(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGather(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGatherNd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGreater(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseGreaterEqual(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseHardSwish(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseIf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseL2Normalization(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLeakyRelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLess(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLessEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLog(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalAnd(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalNot(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogicalOr(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogistic(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLogSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseLSTM(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMaximum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMinimum(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMirrorPad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseMul(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseNeg(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseNotEqual(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePack(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePad(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePadV2(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePool(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePow(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParsePrelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseQuantize(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReadVariable(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReducer(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRelu(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRelu6(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseReshape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseResizeBilinear(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseResizeNearestNeighbor(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRound(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseRsqrt(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSelectV2(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSin(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSlice(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSoftmax(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSpaceToBatchNd(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSpaceToDepth(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSplit(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSplitV(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSqueeze(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSqrt(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSquare(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSquaredDifference(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseStridedSlice(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSub(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseSvdf(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTanh(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTranspose(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseTransposeConv(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseUnpack(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseUnidirectionalSequenceLSTM(const Operator* op,
|
||||
ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseVarHandle(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseWhile(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator, void** builtin_data);
|
||||
|
||||
TfLiteStatus ParseZerosLike(const Operator* op, ErrorReporter* error_reporter,
|
||||
BuiltinDataAllocator* allocator,
|
||||
void** builtin_data);
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
|
||||
|
||||
#define FIXED_POINT 16
|
||||
namespace kissfft_fixed16 {
|
||||
#include "kiss_fft.c"
|
||||
#include "tools/kiss_fftr.c"
|
||||
} // namespace kissfft_fixed16
|
||||
#undef FIXED_POINT
|
||||
@@ -1,168 +0,0 @@
|
||||
/* Copyright 2020 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ruy/profiler/instrumentation.h" // from @ruy
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace reference_ops {
|
||||
|
||||
inline int16_t SaturatingLeftShift(int16_t value, int amount) {
|
||||
int64_t result = static_cast<int64_t>(value) * (1 << amount);
|
||||
result = std::min<int64_t>(result, std::numeric_limits<int16_t>::max());
|
||||
result = std::max<int64_t>(result, std::numeric_limits<int16_t>::min());
|
||||
return result;
|
||||
}
|
||||
|
||||
// Similar to ARM instruction SQDMULH.
|
||||
// Similar to gemmlowp::SaturatingRoundingDoublingHighMul except
|
||||
// rounding to zero instead of to nearest (SQRDMULH).
|
||||
inline std::int16_t SaturatingDoublingHighMul(std::int16_t a, std::int16_t b) {
|
||||
bool overflow = a == b && a == std::numeric_limits<std::int16_t>::min();
|
||||
std::int32_t a_32(a);
|
||||
std::int32_t b_32(b);
|
||||
std::int32_t ab_32 = a_32 * b_32;
|
||||
std::int16_t ab_x2_high16 = static_cast<std::int16_t>((ab_32) / (1 << 15));
|
||||
return overflow ? std::numeric_limits<std::int16_t>::max() : ab_x2_high16;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void HardSwish(const RuntimeShape& input_shape, const T* input_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("ReferenceHardSwish/Float");
|
||||
auto matching_size = MatchingFlatSize(input_shape, output_shape);
|
||||
const T* in_end = input_data + matching_size;
|
||||
for (; input_data < in_end; input_data++, output_data++) {
|
||||
const float in = *input_data;
|
||||
*output_data =
|
||||
in * std::min(static_cast<T>(6), std::max(static_cast<T>(0), in + 3)) /
|
||||
6;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void HardSwish(const HardSwishParams& params,
|
||||
const RuntimeShape& input_shape, const T* input_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("ReferenceHardSwish/Quantized");
|
||||
|
||||
const int flat_size = MatchingFlatSize(input_shape, output_shape);
|
||||
|
||||
for (int i = 0; i < flat_size; i++) {
|
||||
const int16_t input_value = input_data[i] - params.input_zero_point;
|
||||
// Left-shift as much as we can without overflow/saturation to put
|
||||
// significant bits in the high bits of our 16-bit fixedpoint values, so
|
||||
// that fixed-point approximate computations below are as accurate as
|
||||
// possible.
|
||||
const int16_t input_value_on_hires_input_scale = input_value * (1 << 7);
|
||||
// Compute the input value on essentially the output scale, just not
|
||||
// right-shifted yet. This is the value that we'll use in the (x >= +3)
|
||||
// case, and that in the general case we'll multiply against the "relu-ish"
|
||||
// fixed-point multiplier in [0, 1].
|
||||
const int16_t input_value_on_preshift_output_scale =
|
||||
gemmlowp::SaturatingRoundingDoublingHighMul(
|
||||
input_value_on_hires_input_scale,
|
||||
params.output_multiplier_fixedpoint_int16);
|
||||
// Now compute the "relu-ish multiplier". In the (-3 <= x <= +3) case, that
|
||||
// is just an affine rescaling of x from [-3, 3] to [0, 1]. In the general
|
||||
// case, it is just that plus saturation at the boundaries of [-3, 3].
|
||||
// First, we rescale from [-3, 3] to [-1, 1], saturating.
|
||||
// That is done by rescaling the input value with a fixed-point multiplier
|
||||
// (reluish_multiplier_fixedpoint) and bit-shift such that we represent
|
||||
// that input value on the scale where the real value 3.0f is represented
|
||||
// by the quantized value 32768. (+32768 is actually not representable as
|
||||
// int16_t, so this saturates at +32767, and that is seen empirically to be
|
||||
// a negligible contribution to numerical error/bias).
|
||||
//
|
||||
// This code is careful to correctly implement any magnitude of multiplier,
|
||||
// involving either a right shift or a left shift, with correct saturation
|
||||
// behavior in the left-shift case. This forces this code to be more
|
||||
// complicated, but is necessary for real applications: a partially
|
||||
// trained quantized MobileNet v3-small model that motivated this code
|
||||
// exhibits some large [min, max] range boundaries, of the order of
|
||||
// magnitude of 10 or 100 depending on layers.
|
||||
//
|
||||
// The next few lines are basically just an ordinary
|
||||
// MultiplyByQuantizedMultiplier, except that we are more careful here
|
||||
// about the fine details of saturation when left-shifting, because here
|
||||
// overflow in left-shift is a common case, not an anomaly as
|
||||
// MultiplyByQuantizedMultiplier assumes.
|
||||
int16_t reluish_value = input_value_on_hires_input_scale;
|
||||
// Shift left, saturating, as much as we can while ensuring that this
|
||||
// saturation will not contribute to the result. That is, left shift amount
|
||||
// reduced by 1.
|
||||
if (params.reluish_multiplier_exponent > 0) {
|
||||
reluish_value = SaturatingLeftShift(
|
||||
reluish_value, params.reluish_multiplier_exponent - 1);
|
||||
}
|
||||
// Apply the fixed-point multiplier, dividing the value by a divisor
|
||||
// ranging in [1, 2].
|
||||
reluish_value = gemmlowp::SaturatingRoundingDoublingHighMul(
|
||||
reluish_value, params.reluish_multiplier_fixedpoint_int16);
|
||||
// Apply the last bit of left-shift. Thus, in the left-shifting case, if
|
||||
// any saturation affects the result, it is happening here --- any
|
||||
// saturation having occurred above is overwritten here, not affecting the
|
||||
// result.
|
||||
if (params.reluish_multiplier_exponent > 0) {
|
||||
reluish_value = SaturatingLeftShift(reluish_value, 1);
|
||||
}
|
||||
// Shift right, in the right-shifting case.
|
||||
if (params.reluish_multiplier_exponent < 0) {
|
||||
reluish_value = gemmlowp::RoundingDivideByPOT(
|
||||
reluish_value, -params.reluish_multiplier_exponent);
|
||||
}
|
||||
// At this point we have rescaled the value into a 16bit fixedpoint
|
||||
// reluish_value in [-1, 1].
|
||||
// We now convert that to a 16bit fixedpoint value in [0, 1].
|
||||
reluish_value = (reluish_value + (1 << 15)) >> 1;
|
||||
// Use of SaturatingDoublingHighMul here is important to cancel the biases
|
||||
// from the above SaturatingRoundingDoublingHighMul.
|
||||
//
|
||||
// On a partially trained MobileNet-v3-small,
|
||||
//
|
||||
// | bias on | ImageNet
|
||||
// | quantized | Top-1
|
||||
// Operation used here | values | accuracy (50k)
|
||||
// --------------------------------------+------------+-----------
|
||||
// SaturatingDoublingHighMul | -0.0024 | 58.920
|
||||
// SaturatingRoundingDoublingHighMul | -0.0067 | 58.064
|
||||
//
|
||||
// In activations_test, this is covered by this testcase:
|
||||
// QuantizedActivationsOpTest.HardSwishBias
|
||||
//
|
||||
const int16_t preshift_output_value = SaturatingDoublingHighMul(
|
||||
reluish_value, input_value_on_preshift_output_scale);
|
||||
// We were so far operating on the pre-shift output scale. Now we finally
|
||||
// apply that output shift, arriving at the final output scale.
|
||||
int16_t output_value = gemmlowp::RoundingDivideByPOT(
|
||||
preshift_output_value, -params.output_multiplier_exponent);
|
||||
output_value += params.output_zero_point;
|
||||
output_value =
|
||||
std::min<int16_t>(output_value, std::numeric_limits<T>::max());
|
||||
output_value =
|
||||
std::max<int16_t>(output_value, std::numeric_limits<T>::min());
|
||||
output_data[i] = output_value;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
|
||||
@@ -1,214 +0,0 @@
|
||||
/* 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_MUL_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_MUL_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace reference_ops {
|
||||
|
||||
// Element-wise mul that can often be used for inner loop of broadcast Mul as
|
||||
// well as the non-broadcast Mul.
|
||||
inline void MulElementwise(int size, const ArithmeticParams& params,
|
||||
const uint8_t* input1_data,
|
||||
const uint8_t* input2_data, uint8_t* output_data) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const int32_t input1_val = params.input1_offset + input1_data[i];
|
||||
const int32_t input2_val = params.input2_offset + input2_data[i];
|
||||
const int32_t unclamped_result =
|
||||
params.output_offset +
|
||||
MultiplyByQuantizedMultiplier(input1_val * input2_val,
|
||||
params.output_multiplier,
|
||||
params.output_shift);
|
||||
const int32_t clamped_output =
|
||||
std::min(params.quantized_activation_max,
|
||||
std::max(params.quantized_activation_min, unclamped_result));
|
||||
output_data[i] = static_cast<uint8_t>(clamped_output);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Mul(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape, const T* input1_data,
|
||||
const RuntimeShape& input2_shape, const T* input2_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
T output_activation_min;
|
||||
T output_activation_max;
|
||||
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
||||
|
||||
const int flat_size =
|
||||
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||
for (int i = 0; i < flat_size; ++i) {
|
||||
output_data[i] = ActivationFunctionWithMinMax(
|
||||
input1_data[i] * input2_data[i], output_activation_min,
|
||||
output_activation_max);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Mul(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape,
|
||||
const std::complex<float>* input1_data,
|
||||
const RuntimeShape& input2_shape,
|
||||
const std::complex<float>* input2_data,
|
||||
const RuntimeShape& output_shape,
|
||||
std::complex<float>* output_data) {
|
||||
const int flat_size =
|
||||
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||
for (int i = 0; i < flat_size; ++i) {
|
||||
output_data[i] = input1_data[i] * input2_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
inline void Mul(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape, const uint8_t* input1_data,
|
||||
const RuntimeShape& input2_shape, const uint8_t* input2_data,
|
||||
const RuntimeShape& output_shape, uint8_t* output_data) {
|
||||
TFLITE_DCHECK_LE(params.quantized_activation_min,
|
||||
params.quantized_activation_max);
|
||||
const int flat_size =
|
||||
MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
|
||||
|
||||
MulElementwise(flat_size, params, input1_data, input2_data, output_data);
|
||||
}
|
||||
|
||||
inline void BroadcastMul4DSlow(const ArithmeticParams& params,
|
||||
const RuntimeShape& input1_shape,
|
||||
const uint8_t* input1_data,
|
||||
const RuntimeShape& input2_shape,
|
||||
const uint8_t* input2_data,
|
||||
const RuntimeShape& output_shape,
|
||||
uint8_t* output_data) {
|
||||
NdArrayDesc<4> desc1;
|
||||
NdArrayDesc<4> desc2;
|
||||
NdArrayDescsForElementwiseBroadcast(input1_shape, input2_shape, &desc1,
|
||||
&desc2);
|
||||
const RuntimeShape extended_output_shape =
|
||||
RuntimeShape::ExtendedShape(4, output_shape);
|
||||
|
||||
for (int b = 0; b < extended_output_shape.Dims(0); ++b) {
|
||||
for (int y = 0; y < extended_output_shape.Dims(1); ++y) {
|
||||
for (int x = 0; x < extended_output_shape.Dims(2); ++x) {
|
||||
for (int c = 0; c < extended_output_shape.Dims(3); ++c) {
|
||||
const int32_t input1_val =
|
||||
params.input1_offset +
|
||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)];
|
||||
const int32_t input2_val =
|
||||
params.input2_offset +
|
||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)];
|
||||
const int32_t unclamped_result =
|
||||
params.output_offset +
|
||||
MultiplyByQuantizedMultiplier(input1_val * input2_val,
|
||||
params.output_multiplier,
|
||||
params.output_shift);
|
||||
const int32_t clamped_output = std::min(
|
||||
params.quantized_activation_max,
|
||||
std::max(params.quantized_activation_min, unclamped_result));
|
||||
output_data[Offset(extended_output_shape, b, y, x, c)] =
|
||||
static_cast<uint8_t>(clamped_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void BroadcastMul4DSlow(const ArithmeticParams& params,
|
||||
const RuntimeShape& unextended_input1_shape,
|
||||
const T* input1_data,
|
||||
const RuntimeShape& unextended_input2_shape,
|
||||
const T* input2_data,
|
||||
const RuntimeShape& unextended_output_shape,
|
||||
T* output_data) {
|
||||
T output_activation_min;
|
||||
T output_activation_max;
|
||||
GetActivationParams(params, &output_activation_min, &output_activation_max);
|
||||
|
||||
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||
const RuntimeShape output_shape =
|
||||
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||
|
||||
NdArrayDesc<4> desc1;
|
||||
NdArrayDesc<4> desc2;
|
||||
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
|
||||
unextended_input2_shape, &desc1, &desc2);
|
||||
|
||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
||||
// col, channel), with extents (batches, height, width, depth), with the
|
||||
// trailing dimension changing most rapidly (channels has the smallest stride,
|
||||
// typically 1 element).
|
||||
//
|
||||
// In generated C code, we store arrays with the dimensions reversed. The
|
||||
// first dimension has smallest stride.
|
||||
//
|
||||
// We name our variables by their Tensorflow convention, but generate C code
|
||||
// nesting loops such that the innermost loop has the smallest stride for the
|
||||
// best cache behavior.
|
||||
for (int b = 0; b < output_shape.Dims(0); ++b) {
|
||||
for (int y = 0; y < output_shape.Dims(1); ++y) {
|
||||
for (int x = 0; x < output_shape.Dims(2); ++x) {
|
||||
for (int c = 0; c < output_shape.Dims(3); ++c) {
|
||||
output_data[Offset(output_shape, b, y, x, c)] =
|
||||
ActivationFunctionWithMinMax(
|
||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)] *
|
||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)],
|
||||
output_activation_min, output_activation_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void BroadcastMul4DSlow(const ArithmeticParams& params,
|
||||
const RuntimeShape& unextended_input1_shape,
|
||||
const std::complex<float>* input1_data,
|
||||
const RuntimeShape& unextended_input2_shape,
|
||||
const std::complex<float>* input2_data,
|
||||
const RuntimeShape& unextended_output_shape,
|
||||
std::complex<float>* output_data) {
|
||||
TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
|
||||
const RuntimeShape output_shape =
|
||||
RuntimeShape::ExtendedShape(4, unextended_output_shape);
|
||||
|
||||
NdArrayDesc<4> desc1;
|
||||
NdArrayDesc<4> desc2;
|
||||
NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
|
||||
unextended_input2_shape, &desc1, &desc2);
|
||||
|
||||
for (int b = 0; b < output_shape.Dims(0); ++b) {
|
||||
for (int y = 0; y < output_shape.Dims(1); ++y) {
|
||||
for (int x = 0; x < output_shape.Dims(2); ++x) {
|
||||
for (int c = 0; c < output_shape.Dims(3); ++c) {
|
||||
output_data[Offset(output_shape, b, y, x, c)] =
|
||||
input1_data[SubscriptToIndex(desc1, b, y, x, c)] *
|
||||
input2_data[SubscriptToIndex(desc2, b, y, x, c)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_MUL_H_
|
||||
@@ -1,151 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "ruy/profiler/instrumentation.h" // from @ruy
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace reference_ops {
|
||||
|
||||
template <typename D, typename T>
|
||||
void Select(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data, const RuntimeShape& input_x_shape,
|
||||
const T* input_x_data, const RuntimeShape& input_y_shape,
|
||||
const T* input_y_data, const RuntimeShape& output_shape,
|
||||
T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select");
|
||||
int64_t flatsize;
|
||||
// Allow select operator executions on mixed scalar tensors and one element
|
||||
// tensors.
|
||||
if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 &&
|
||||
input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) {
|
||||
flatsize = 1;
|
||||
} else {
|
||||
flatsize = MatchingFlatSize(input_condition_shape, input_x_shape,
|
||||
input_y_shape, output_shape);
|
||||
}
|
||||
for (int64_t i = 0; i < flatsize; ++i) {
|
||||
output_data[i] =
|
||||
input_condition_data[i] ? input_x_data[i] : input_y_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
void RankOneSelect(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data,
|
||||
const RuntimeShape& input_x_shape, const T* input_x_data,
|
||||
const RuntimeShape& input_y_shape, const T* input_y_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select/RankOneSelect");
|
||||
const int64_t outer_size = input_condition_shape.FlatSize();
|
||||
int64_t inner_size;
|
||||
if (input_condition_shape.DimensionsCount() == 0) {
|
||||
inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape);
|
||||
} else {
|
||||
TFLITE_DCHECK_EQ(
|
||||
MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0),
|
||||
outer_size);
|
||||
inner_size =
|
||||
MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape);
|
||||
}
|
||||
|
||||
int64_t offset = 0;
|
||||
for (int64_t i = 0; i < outer_size; i++) {
|
||||
const T* input_data = input_condition_data[i] ? input_x_data : input_y_data;
|
||||
memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T));
|
||||
offset += inner_size;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename D, typename T>
|
||||
void BroadcastSelect5DSlow(const RuntimeShape& input_condition_shape,
|
||||
const D* input_condition_data,
|
||||
const RuntimeShape& input_x_shape,
|
||||
const T* input_x_data,
|
||||
const RuntimeShape& input_y_shape,
|
||||
const T* input_y_data,
|
||||
const RuntimeShape& output_shape, T* output_data) {
|
||||
ruy::profiler::ScopeLabel label("Select/BroadcastSelectSlow");
|
||||
TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 5);
|
||||
TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 5);
|
||||
|
||||
NdArrayDesc<5> desc_condition;
|
||||
NdArrayDesc<5> desc_x;
|
||||
NdArrayDesc<5> desc_y;
|
||||
NdArrayDesc<5> desc_output;
|
||||
const RuntimeShape extended_output_shape =
|
||||
RuntimeShape::ExtendedShape(5, output_shape);
|
||||
CopyDimsToDesc(extended_output_shape, &desc_output);
|
||||
NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape,
|
||||
input_y_shape, &desc_condition, &desc_x,
|
||||
&desc_y);
|
||||
|
||||
// In Tensorflow, the dimensions are canonically named (batch_number, row,
|
||||
// col, channel), with extents (batches, height, width, depth), with the
|
||||
// trailing dimension changing most rapidly (channels has the smallest
|
||||
// stride, typically 1 element).
|
||||
//
|
||||
// In generated C code, we store arrays with the dimensions reversed. The
|
||||
// first dimension has smallest stride.
|
||||
//
|
||||
// We name our variables by their Tensorflow convention, but generate C code
|
||||
// nesting loops such that the innermost loop has the smallest stride for
|
||||
// the best cache behavior.
|
||||
for (int n = 0; n < desc_output.extents[0]; ++n) {
|
||||
int out_idx_n = desc_output.extents[1] * n;
|
||||
int cond_idx_n = desc_condition.strides[0] * n;
|
||||
int in_idx1_n = desc_x.strides[0] * n;
|
||||
int in_idx2_n = desc_y.strides[0] * n;
|
||||
for (int b = 0; b < desc_output.extents[1]; ++b) {
|
||||
int out_idx_b = (out_idx_n + b) * desc_output.extents[2];
|
||||
int cond_idx_b = cond_idx_n + desc_condition.strides[1] * b;
|
||||
int in_idx1_b = in_idx1_n + desc_x.strides[1] * b;
|
||||
int in_idx2_b = in_idx2_n + desc_y.strides[1] * b;
|
||||
for (int y = 0; y < desc_output.extents[2]; ++y) {
|
||||
int out_idx_y = (out_idx_b + y) * desc_output.extents[3];
|
||||
int cond_idx_y = cond_idx_b + desc_condition.strides[2] * y;
|
||||
int in_idx1_y = in_idx1_b + desc_x.strides[2] * y;
|
||||
int in_idx2_y = in_idx2_b + desc_y.strides[2] * y;
|
||||
for (int x = 0; x < desc_output.extents[3]; ++x) {
|
||||
int out_idx = (out_idx_y + x) * desc_output.extents[4];
|
||||
int cond_idx = cond_idx_y + desc_condition.strides[3] * x;
|
||||
int in_idx1 = in_idx1_y + desc_x.strides[3] * x;
|
||||
int in_idx2 = in_idx2_y + desc_y.strides[3] * x;
|
||||
for (int c = 0; c < desc_output.extents[4]; ++c) {
|
||||
output_data[out_idx] = input_condition_data[cond_idx]
|
||||
? input_x_data[in_idx1]
|
||||
: input_y_data[in_idx2];
|
||||
out_idx++;
|
||||
cond_idx += desc_condition.strides[4];
|
||||
in_idx1 += desc_x.strides[4];
|
||||
in_idx2 += desc_y.strides[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reference_ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
|
||||
@@ -1,122 +0,0 @@
|
||||
/* Copyright 2018 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/micro/all_ops_resolver.h"
|
||||
|
||||
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
AllOpsResolver::AllOpsResolver() {
|
||||
// Please keep this list of Builtin Operators in alphabetical order.
|
||||
AddAbs();
|
||||
AddAdd();
|
||||
AddAddN();
|
||||
AddArgMax();
|
||||
AddArgMin();
|
||||
AddAssignVariable();
|
||||
AddAveragePool2D();
|
||||
AddBatchToSpaceNd();
|
||||
AddBroadcastArgs();
|
||||
AddBroadcastTo();
|
||||
AddCallOnce();
|
||||
AddCast();
|
||||
AddCeil();
|
||||
AddCircularBuffer();
|
||||
AddConcatenation();
|
||||
AddConv2D();
|
||||
AddCos();
|
||||
AddCumSum();
|
||||
AddDepthToSpace();
|
||||
AddDepthwiseConv2D();
|
||||
AddDequantize();
|
||||
AddDetectionPostprocess();
|
||||
AddDiv();
|
||||
AddElu();
|
||||
AddEqual();
|
||||
AddEthosU();
|
||||
AddExp();
|
||||
AddExpandDims();
|
||||
AddFill();
|
||||
AddFloor();
|
||||
AddFloorDiv();
|
||||
AddFloorMod();
|
||||
AddFullyConnected();
|
||||
AddGather();
|
||||
AddGatherNd();
|
||||
AddGreater();
|
||||
AddGreaterEqual();
|
||||
AddHardSwish();
|
||||
AddIf();
|
||||
AddL2Normalization();
|
||||
AddL2Pool2D();
|
||||
AddLeakyRelu();
|
||||
AddLess();
|
||||
AddLessEqual();
|
||||
AddLog();
|
||||
AddLogicalAnd();
|
||||
AddLogicalNot();
|
||||
AddLogicalOr();
|
||||
AddLogistic();
|
||||
AddMaxPool2D();
|
||||
AddMaximum();
|
||||
AddMean();
|
||||
AddMinimum();
|
||||
AddMirrorPad();
|
||||
AddMul();
|
||||
AddNeg();
|
||||
AddNotEqual();
|
||||
AddPack();
|
||||
AddPad();
|
||||
AddPadV2();
|
||||
AddPrelu();
|
||||
AddQuantize();
|
||||
AddReadVariable();
|
||||
AddReduceMax();
|
||||
AddRelu();
|
||||
AddRelu6();
|
||||
AddReshape();
|
||||
AddResizeBilinear();
|
||||
AddResizeNearestNeighbor();
|
||||
AddRound();
|
||||
AddRsqrt();
|
||||
AddSelectV2();
|
||||
AddShape();
|
||||
AddSin();
|
||||
AddSlice();
|
||||
AddSoftmax();
|
||||
AddSpaceToBatchNd();
|
||||
AddSpaceToDepth();
|
||||
AddSplit();
|
||||
AddSplitV();
|
||||
AddSqrt();
|
||||
AddSquare();
|
||||
AddSquaredDifference();
|
||||
AddSqueeze();
|
||||
AddStridedSlice();
|
||||
AddSub();
|
||||
AddSum();
|
||||
AddSvdf();
|
||||
AddTanh();
|
||||
AddTranspose();
|
||||
AddTransposeConv();
|
||||
AddUnidirectionalSequenceLSTM();
|
||||
AddUnpack();
|
||||
AddVarHandle();
|
||||
AddWhile();
|
||||
AddZerosLike();
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,77 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
extern const int kAddInputTensor1;
|
||||
extern const int kAddInputTensor2;
|
||||
extern const int kAddOutputTensor;
|
||||
|
||||
struct OpDataAdd {
|
||||
bool requires_broadcast;
|
||||
|
||||
// These fields are used in both the general 8-bit -> 8bit quantized path,
|
||||
// and the special 16-bit -> 16bit quantized path
|
||||
int input1_shift;
|
||||
int input2_shift;
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
|
||||
// These fields are used only in the general 8-bit -> 8bit quantized path
|
||||
int32_t input1_multiplier;
|
||||
int32_t input2_multiplier;
|
||||
int32_t output_multiplier;
|
||||
int output_shift;
|
||||
int left_shift;
|
||||
int32_t input1_offset;
|
||||
int32_t input2_offset;
|
||||
int32_t output_offset;
|
||||
|
||||
// Used only for float evals:
|
||||
float output_activation_min_f32;
|
||||
float output_activation_max_f32;
|
||||
};
|
||||
|
||||
TfLiteStatus CalculateOpDataAdd(TfLiteContext* context, TfLiteAddParams* params,
|
||||
const TfLiteTensor* input1,
|
||||
const TfLiteTensor* input2,
|
||||
TfLiteTensor* output, OpDataAdd* data);
|
||||
|
||||
TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// Generic must define registration function.
|
||||
TfLiteRegistration Register_ADD();
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_ADD_INT8();
|
||||
|
||||
TfLiteRegistration Register_ADD_INT16();
|
||||
#else
|
||||
// Fallback registration
|
||||
inline TfLiteRegistration Register_ADD_INT8() { return Register_ADD(); }
|
||||
|
||||
inline TfLiteRegistration Register_ADD_INT16() { return Register_ADD(); }
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_
|
||||
@@ -1,214 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/add_n.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor0 = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
constexpr int kAddNIntegerShift = 20;
|
||||
|
||||
// only used with INT8 tensors
|
||||
struct OpData {
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
int32_t input_offset;
|
||||
int32_t output_offset;
|
||||
int32_t input_multiplier;
|
||||
int32_t output_multiplier;
|
||||
int input_shift;
|
||||
int output_shift;
|
||||
int left_shift;
|
||||
int scratch_index;
|
||||
};
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
int num_inputs = NumInputs(node);
|
||||
TF_LITE_ENSURE(context, num_inputs >= 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input_tensor_first =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor0);
|
||||
TF_LITE_ENSURE(context, input_tensor_first != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
// Check that all tensors have the same shape and type.
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, output->type, input_tensor_first->type);
|
||||
for (int i = kInputTensor0 + 1; i < num_inputs; ++i) {
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, i);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TF_LITE_ENSURE(context, HaveSameShapes(input_tensor_first, input));
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input_tensor_first->type, input->type);
|
||||
|
||||
// Check that all INT8 input tensors have the same zero-point and scale.
|
||||
if (input_tensor_first->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE(context, input_tensor_first->params.zero_point ==
|
||||
input->params.zero_point);
|
||||
TF_LITE_ENSURE(context,
|
||||
input_tensor_first->params.scale == input->params.scale);
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
}
|
||||
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
// Allocate scratch buffer space for pointer to each tensor's data
|
||||
// and store the scratch buffer index in the node's user_data
|
||||
int scratch_index;
|
||||
size_t scratch_size = sizeof(float*) * num_inputs;
|
||||
TF_LITE_ENSURE_OK(context, context->RequestScratchBufferInArena(
|
||||
context, scratch_size, &scratch_index));
|
||||
node->user_data =
|
||||
reinterpret_cast<decltype(node->user_data)>(scratch_index);
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
node->user_data =
|
||||
context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
// Allocate scratch buffer space for pointer to each tensor's data
|
||||
// and store the scratch buffer index in OpData
|
||||
size_t scratch_size = sizeof(int8_t*) * num_inputs;
|
||||
TF_LITE_ENSURE_OK(
|
||||
context, context->RequestScratchBufferInArena(context, scratch_size,
|
||||
&data->scratch_index));
|
||||
|
||||
// 8bit -> 8bit general quantized path, with general rescalings
|
||||
data->input_offset = -input_tensor_first->params.zero_point;
|
||||
data->output_offset = output->params.zero_point;
|
||||
data->left_shift = kAddNIntegerShift;
|
||||
const double twice_max_input_scale =
|
||||
2 * static_cast<double>(input_tensor_first->params.scale);
|
||||
const double real_input_multiplier =
|
||||
static_cast<double>(input_tensor_first->params.scale) /
|
||||
twice_max_input_scale;
|
||||
const double real_output_multiplier =
|
||||
twice_max_input_scale /
|
||||
((1 << data->left_shift) * static_cast<double>(output->params.scale));
|
||||
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
real_input_multiplier, &data->input_multiplier, &data->input_shift);
|
||||
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
real_output_multiplier, &data->output_multiplier, &data->output_shift);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized(
|
||||
context, kTfLiteActNone, output, &data->output_activation_min,
|
||||
&data->output_activation_max));
|
||||
} else {
|
||||
MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input_tensor_first);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline const T** CopyInputsToScratchBuffer(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
const int scratch_index) {
|
||||
int num_inputs = NumInputs(node);
|
||||
void* scratch_buffer = context->GetScratchBuffer(context, scratch_index);
|
||||
const T** all_inputs = static_cast<decltype(all_inputs)>(scratch_buffer);
|
||||
for (int i = 0; i < num_inputs; i++) {
|
||||
const TfLiteEvalTensor* next_input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor0 + i);
|
||||
all_inputs[i] = tflite::micro::GetTensorData<T>(next_input);
|
||||
}
|
||||
|
||||
return all_inputs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EvalAddN(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteEvalTensor* output) {
|
||||
int num_inputs = NumInputs(node);
|
||||
|
||||
int scratch_index =
|
||||
static_cast<int>(reinterpret_cast<intptr_t>(node->user_data));
|
||||
const T** all_inputs =
|
||||
CopyInputsToScratchBuffer<T>(context, node, scratch_index);
|
||||
|
||||
reference_ops::AddN<T>(tflite::micro::GetTensorShape(output), num_inputs,
|
||||
all_inputs, tflite::micro::GetTensorData<T>(output));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EvalAddNQuantized(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteEvalTensor* output) {
|
||||
int num_inputs = NumInputs(node);
|
||||
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
const T** all_inputs =
|
||||
CopyInputsToScratchBuffer<T>(context, node, data->scratch_index);
|
||||
|
||||
ArithmeticParams params;
|
||||
params.left_shift = data->left_shift;
|
||||
params.input1_offset = data->input_offset;
|
||||
params.input1_multiplier = data->input_multiplier;
|
||||
params.input1_shift = data->input_shift;
|
||||
params.output_offset = data->output_offset;
|
||||
params.output_multiplier = data->output_multiplier;
|
||||
params.output_shift = data->output_shift;
|
||||
SetActivationParams(data->output_activation_min, data->output_activation_max,
|
||||
¶ms);
|
||||
|
||||
reference_ops::AddN(params, tflite::micro::GetTensorShape(output), num_inputs,
|
||||
all_inputs, tflite::micro::GetTensorData<T>(output));
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
EvalAddN<float>(context, node, output);
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
EvalAddNQuantized<int8_t>(context, node, output);
|
||||
} else {
|
||||
MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_ADD_N() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,115 +0,0 @@
|
||||
/* Copyright 2018 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/kernels/internal/reference/arg_min_max.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace arg_min_max {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kAxis = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
template <typename T1, typename T2, typename T3>
|
||||
inline void ArgMinMaxHelper(const RuntimeShape& input1_shape,
|
||||
const T1* input1_data, const T3* input2_data,
|
||||
const RuntimeShape& output_shape, T2* output_data,
|
||||
bool is_arg_max) {
|
||||
if (is_arg_max) {
|
||||
reference_ops::ArgMinMax(input1_shape, input1_data, input2_data,
|
||||
output_shape, output_data, micro::Greater());
|
||||
} else {
|
||||
reference_ops::ArgMinMax(input1_shape, input1_data, input2_data,
|
||||
output_shape, output_data, micro::Less());
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node, bool is_arg_max) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* axis =
|
||||
tflite::micro::GetEvalInput(context, node, kAxis);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
#define TF_LITE_ARG_MIN_MAX(data_type, axis_type, output_type) \
|
||||
ArgMinMaxHelper(tflite::micro::GetTensorShape(input), \
|
||||
tflite::micro::GetTensorData<data_type>(input), \
|
||||
tflite::micro::GetTensorData<axis_type>(axis), \
|
||||
tflite::micro::GetTensorShape(output), \
|
||||
tflite::micro::GetTensorData<output_type>(output), \
|
||||
is_arg_max)
|
||||
if (axis->type == kTfLiteInt32) {
|
||||
if (output->type == kTfLiteInt32) {
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
TF_LITE_ARG_MIN_MAX(float, int32_t, int32_t);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
TF_LITE_ARG_MIN_MAX(int8_t, int32_t, int32_t);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf(
|
||||
"Only float32, uint8_t and int8_t are "
|
||||
"supported currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
MicroPrintf("Only int32_t are supported currently, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
MicroPrintf("Only int32_t are supported currently, got %s.",
|
||||
TfLiteTypeGetName(axis->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
#undef TF_LITE_ARG_MIN_MAX
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus ArgMinEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return Eval(context, node, false);
|
||||
}
|
||||
|
||||
TfLiteStatus ArgMaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return Eval(context, node, true);
|
||||
}
|
||||
|
||||
} // namespace arg_min_max
|
||||
|
||||
TfLiteRegistration Register_ARG_MAX() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, arg_min_max::ArgMaxEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_ARG_MIN() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, arg_min_max::ArgMinEval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,111 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/batch_to_space_nd.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kBlockShapeTensor = 1;
|
||||
constexpr int kCropsTensor = 2;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// Currently, only 3D NHC and 4D NHWC input/output op_context are supported.
|
||||
// In case of 3D input, it will be extended to 3D NHWC by adding W=1.
|
||||
// The 4D array need to have exactly 2 spatial dimensions.
|
||||
// TODO(b/149952582): Support arbitrary dimension in SpaceToBatchND.
|
||||
const int kInputOutputMinDimensionNum = 3;
|
||||
const int kInputOutputMaxDimensionNum = 4;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr && output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) >= kInputOutputMinDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(output) >= kInputOutputMinDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) <= kInputOutputMaxDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(output) <= kInputOutputMaxDimensionNum);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* block_shape =
|
||||
tflite::micro::GetEvalInput(context, node, kBlockShapeTensor);
|
||||
const TfLiteEvalTensor* crops =
|
||||
tflite::micro::GetEvalInput(context, node, kCropsTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::BatchToSpaceND(
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(block_shape),
|
||||
tflite::micro::GetTensorData<int32_t>(block_shape),
|
||||
tflite::micro::GetTensorShape(crops),
|
||||
tflite::micro::GetTensorData<int32_t>(crops),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::BatchToSpaceND(
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(block_shape),
|
||||
tflite::micro::GetTensorData<int32_t>(block_shape),
|
||||
tflite::micro::GetTensorShape(crops),
|
||||
tflite::micro::GetTensorData<int32_t>(crops),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace.
|
||||
|
||||
TfLiteRegistration Register_BATCH_TO_SPACE_ND() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,116 +0,0 @@
|
||||
/* Copyright 2020 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/micro/kernels/circular_buffer.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/flatbuffer_utils.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
/*
|
||||
* The circular buffer custom operator is used to implement strided streaming
|
||||
* convolutions on TFLite Micro. Each time this operator is invoked, it checks
|
||||
* whether or not to run, based on a predetermined stride in time. If the op
|
||||
* runs, it inserts the input into the end of the output buffer and shifts the
|
||||
* output values towards the start of the buffer. It discards the oldest value
|
||||
* in the output buffer.
|
||||
*
|
||||
* Input: [<input N+1]
|
||||
* Before shifting:
|
||||
* Output: [<input 1>, <input 2>, <input ...>, <input N>]
|
||||
*
|
||||
* After shifting:
|
||||
* Output: [<input 2>, <input 3>, <input ...>, <input N+1>]
|
||||
*
|
||||
* We make some assumptions in this custom operator:
|
||||
* - Input shape must be [1, 1, 1, depth]
|
||||
* - Output shape must be [1, num_slots, 1, depth]
|
||||
* - Input and output types must match.
|
||||
* - Input and output quantization params must be identical.
|
||||
*/
|
||||
namespace tflite {
|
||||
|
||||
void* CircularBufferInit(TfLiteContext* context, const char* buffer,
|
||||
size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
OpDataCircularBuffer* op_data = static_cast<OpDataCircularBuffer*>(
|
||||
context->AllocatePersistentBuffer(context, sizeof(OpDataCircularBuffer)));
|
||||
|
||||
if (buffer != nullptr && length > 0) {
|
||||
const uint8_t* buffer_t = reinterpret_cast<const uint8_t*>(buffer);
|
||||
tflite::FlexbufferWrapper wrapper(buffer_t, length);
|
||||
op_data->cycles_max = wrapper.ElementAsInt32(kCircularBufferCyclesMaxIndex);
|
||||
} else {
|
||||
op_data->cycles_max = 0;
|
||||
}
|
||||
|
||||
return op_data;
|
||||
}
|
||||
|
||||
// Shifts buffer over by the output depth, and write new input to end of buffer.
|
||||
// num_slots is the number of samples stored in the output buffer.
|
||||
// depth is the size of each sample.
|
||||
void EvalInt8(const int8_t* input, int num_slots, int depth, int8_t* output) {
|
||||
memmove(output, &output[depth], (num_slots - 1) * depth);
|
||||
memcpy(&output[(num_slots - 1) * depth], input, depth);
|
||||
}
|
||||
|
||||
TfLiteStatus CircularBufferEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kCircularBufferInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kCircularBufferOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
OpDataCircularBuffer* data =
|
||||
reinterpret_cast<OpDataCircularBuffer*>(node->user_data);
|
||||
|
||||
int num_slots = output->dims->data[1];
|
||||
int depth = output->dims->data[2] * output->dims->data[3];
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
EvalInt8(tflite::micro::GetTensorData<int8_t>(input), num_slots, depth,
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (--data->cycles_until_run != 0) {
|
||||
// Signal the interpreter to end current run if the delay before op invoke
|
||||
// has not been reached.
|
||||
// TODO(b/149795762): Add kTfLiteAbort to TfLiteStatus enum.
|
||||
return static_cast<TfLiteStatus>(kTfLiteAbort);
|
||||
}
|
||||
|
||||
data->cycles_until_run = data->cycles_max;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteRegistration* Register_CIRCULAR_BUFFER() {
|
||||
static TfLiteRegistration r = tflite::micro::RegisterOp(
|
||||
CircularBufferInit, CircularBufferPrepare, CircularBufferEval);
|
||||
return &r;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,617 +0,0 @@
|
||||
/* 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/kernels/internal/reference/comparisons.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace comparisons {
|
||||
namespace {
|
||||
|
||||
struct OpData {
|
||||
ComparisonParams params;
|
||||
};
|
||||
|
||||
constexpr int kInputTensor1 = 0;
|
||||
constexpr int kInputTensor2 = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus EqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteBool:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<bool>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<bool>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::EqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<bool>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<bool>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::EqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::EqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::EqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::EqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// TODO(renjieliu): Refactor the logic to avoid duplications.
|
||||
TfLiteStatus NotEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteBool:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowNotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<bool>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<bool>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::NotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<bool>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<bool>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowNotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::NotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowNotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::NotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowNotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::NotEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowNotEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::NotEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GreaterEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus GreaterEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowGreaterEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::GreaterEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus LessEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus LessEqualEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
RuntimeShape input1_shape = tflite::micro::GetTensorShape(input1);
|
||||
RuntimeShape input2_shape = tflite::micro::GetTensorShape(input2);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
bool* output_data = tflite::micro::GetTensorData<bool>(output);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<float>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<float>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int32_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessEqualNoScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int64_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
requires_broadcast
|
||||
? reference_ops::Broadcast4DSlowLessEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data)
|
||||
: reference_ops::LessEqualWithScaling(
|
||||
data->params, input1_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input1), input2_shape,
|
||||
tflite::micro::GetTensorData<int8_t>(input2), output_shape,
|
||||
output_data);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input1->type), input1->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input1 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor1);
|
||||
TF_LITE_ENSURE(context, input1 != nullptr);
|
||||
TfLiteTensor* input2 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor2);
|
||||
TF_LITE_ENSURE(context, input2 != nullptr);
|
||||
|
||||
if (input1->type == kTfLiteInt8) {
|
||||
auto input1_offset = -input1->params.zero_point;
|
||||
auto input2_offset = -input2->params.zero_point;
|
||||
const int kLeftShift = 8;
|
||||
|
||||
int32_t input1_multiplier;
|
||||
int input1_shift;
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
static_cast<double>(input1->params.scale), &input1_multiplier,
|
||||
&input1_shift);
|
||||
int32_t input2_multiplier;
|
||||
int input2_shift;
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
static_cast<double>(input2->params.scale), &input2_multiplier,
|
||||
&input2_shift);
|
||||
|
||||
data->params.left_shift = kLeftShift;
|
||||
data->params.input1_offset = input1_offset;
|
||||
data->params.input1_multiplier = input1_multiplier;
|
||||
data->params.input1_shift = input1_shift;
|
||||
data->params.input2_offset = input2_offset;
|
||||
data->params.input2_multiplier = input2_multiplier;
|
||||
data->params.input2_shift = input2_shift;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input1);
|
||||
micro_context->DeallocateTempTfLiteTensor(input2);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace comparisons
|
||||
|
||||
TfLiteRegistration Register_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::EqualEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_NOT_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::NotEqualEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_GREATER() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::GreaterEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_GREATER_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::GreaterEqualEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LESS() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::LessEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LESS_EQUAL() {
|
||||
return tflite::micro::RegisterOp(comparisons::Init, comparisons::Prepare,
|
||||
comparisons::LessEqualEval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,262 +0,0 @@
|
||||
/* 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/kernels/internal/reference/concatenation.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/portable_tensor.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace concatenation {
|
||||
|
||||
constexpr int kMaxInputNum = 10; // Maximum number of input tensors
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpData {
|
||||
ConcatenationParams params;
|
||||
};
|
||||
|
||||
// Handles negative axis index, coerces to positive index value.
|
||||
inline int CalculatePositiveAxis(int axis, const TfLiteTensor* output_tensor) {
|
||||
if (axis >= 0) {
|
||||
return axis;
|
||||
} else {
|
||||
return NumDimensions(output_tensor) + axis;
|
||||
}
|
||||
}
|
||||
|
||||
// The following functions are helpers to get tensor data in the format that the
|
||||
// reference op implementation expects. They provide the same functionality as
|
||||
// class VectorOfTensors and class VectorOfQuantizedTensors in TFLite.
|
||||
|
||||
// Gets shapes from a list of tensors.
|
||||
inline void GetAllInputTensorShapes(const TfLiteContext* context,
|
||||
const TfLiteNode* node,
|
||||
RuntimeShape all_shapes[kMaxInputNum]) {
|
||||
TFLITE_DCHECK(context != nullptr);
|
||||
TFLITE_DCHECK(node != nullptr);
|
||||
for (int i = 0; i < node->inputs->size; ++i) {
|
||||
const TfLiteEvalTensor* t = tflite::micro::GetEvalInput(context, node, i);
|
||||
RuntimeShape shape = tflite::micro::GetTensorShape(t);
|
||||
all_shapes[i].ReplaceWith(shape.DimensionsCount(), shape.DimsData());
|
||||
}
|
||||
}
|
||||
|
||||
// Get shape pointers from a list of shapes.
|
||||
inline void GetShapesPointers(const RuntimeShape* shapes, size_t num,
|
||||
const RuntimeShape* pointers[]) {
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
pointers[i] = &shapes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Gets data pointers from a list of tensors.
|
||||
template <typename T>
|
||||
inline void GetAllInputTensorData(const TfLiteContext* context,
|
||||
const TfLiteNode* node,
|
||||
T* all_data[kMaxInputNum]) {
|
||||
TFLITE_DCHECK(context != nullptr);
|
||||
TFLITE_DCHECK(node != nullptr);
|
||||
for (int i = 0; i < node->inputs->size; ++i) {
|
||||
const TfLiteEvalTensor* t = tflite::micro::GetEvalInput(context, node, i);
|
||||
all_data[i] = tflite::micro::GetTensorData<T>(t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename data_type>
|
||||
void EvalUnquantized(TfLiteContext* context, TfLiteNode* node) {
|
||||
// Collect the shapes and data pointer of input tensors
|
||||
RuntimeShape inputs_shape[kMaxInputNum];
|
||||
const RuntimeShape* inputs_shape_ptr[kMaxInputNum];
|
||||
const data_type* inputs_data[kMaxInputNum];
|
||||
GetAllInputTensorShapes(context, node, inputs_shape);
|
||||
GetShapesPointers(inputs_shape, node->inputs->size, inputs_shape_ptr);
|
||||
GetAllInputTensorData(context, node, inputs_data);
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
reference_ops::Concatenation(data->params, inputs_shape_ptr, inputs_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<data_type>(output));
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
// This function only checks the types. Additional shape validations are
|
||||
// performed in the reference implementation called during Eval().
|
||||
const TfLiteConcatenationParams* params =
|
||||
reinterpret_cast<TfLiteConcatenationParams*>(node->builtin_data);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input_tensor = micro_context->AllocateTempInputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, input_tensor != nullptr);
|
||||
TfLiteType input_type = input_tensor->type;
|
||||
TfLiteTensor* output_tensor =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output_tensor != nullptr);
|
||||
TfLiteType output_type = output_tensor->type;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input_tensor);
|
||||
micro_context->DeallocateTempTfLiteTensor(output_tensor);
|
||||
|
||||
// Check activation and input type
|
||||
TF_LITE_ENSURE_EQ(context, params->activation, kTfLiteActNone);
|
||||
TF_LITE_ENSURE(context,
|
||||
input_type == kTfLiteFloat32 || input_type == kTfLiteInt8 ||
|
||||
input_type == kTfLiteInt16 || input_type == kTfLiteInt32 ||
|
||||
input_type == kTfLiteInt64 || input_type == kTfLiteBool);
|
||||
|
||||
// Output type must match input type
|
||||
TF_LITE_ENSURE_EQ(context, output_type, input_type);
|
||||
|
||||
// This implementation does not support large number of input tensors
|
||||
const int num_inputs = NumInputs(node);
|
||||
TF_LITE_ENSURE(context, num_inputs <= kMaxInputNum);
|
||||
|
||||
// Shapes with dimensions >4 are not yet supported with static allocation.
|
||||
for (int i = 0; i < num_inputs; ++i) {
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, i);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
int num_dimensions = NumDimensions(input);
|
||||
|
||||
if (num_dimensions > RuntimeShape::kMaxSmallSize) {
|
||||
MicroPrintf(
|
||||
"Op Concatenation does not currently support num dimensions > %d "
|
||||
"Tensor has %d dimensions.",
|
||||
RuntimeShape::kMaxSmallSize, num_dimensions);
|
||||
return kTfLiteError;
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
}
|
||||
|
||||
// Calculate OpData.
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
switch (output_type) { // Already know in/outtypes are same.
|
||||
case kTfLiteBool:
|
||||
case kTfLiteFloat32:
|
||||
case kTfLiteInt16:
|
||||
case kTfLiteInt32:
|
||||
case kTfLiteInt64: {
|
||||
data->params.axis = CalculatePositiveAxis(params->axis, output);
|
||||
data->params.inputs_count = node->inputs->size;
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
data->params.axis = CalculatePositiveAxis(params->axis, output);
|
||||
data->params.inputs_count = node->inputs->size;
|
||||
|
||||
float* input_scales =
|
||||
reinterpret_cast<float*>(context->AllocatePersistentBuffer(
|
||||
context, node->inputs->size * sizeof(float)));
|
||||
|
||||
int32_t* input_zero_points =
|
||||
reinterpret_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, node->inputs->size * sizeof(int32_t)));
|
||||
|
||||
// Allocate persistent scale and zeropoint buffers.
|
||||
// Store input scale and zero point values in OpParams:
|
||||
for (int i = 0; i < node->inputs->size; ++i) {
|
||||
TfLiteTensor* t = micro_context->AllocateTempInputTensor(node, i);
|
||||
TF_LITE_ENSURE(context, t != nullptr);
|
||||
input_scales[i] = t->params.scale;
|
||||
input_zero_points[i] = t->params.zero_point;
|
||||
micro_context->DeallocateTempTfLiteTensor(t);
|
||||
}
|
||||
|
||||
data->params.input_scale = input_scales;
|
||||
data->params.input_zeropoint = input_zero_points;
|
||||
data->params.output_zeropoint = output->params.zero_point;
|
||||
data->params.output_scale = output->params.scale;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Op Concatenation does not currently support Type '%s'.",
|
||||
TfLiteTypeGetName(output_type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* output_tensor =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output_tensor != nullptr);
|
||||
TfLiteType output_type = output_tensor->type;
|
||||
|
||||
switch (output_type) { // Already know in/outtypes are same.
|
||||
case kTfLiteFloat32:
|
||||
EvalUnquantized<float>(context, node);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
EvalUnquantized<int32_t>(context, node);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
EvalUnquantized<int8_t>(context, node);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
EvalUnquantized<int64_t>(context, node);
|
||||
break;
|
||||
case kTfLiteInt16:
|
||||
EvalUnquantized<int16_t>(context, node);
|
||||
break;
|
||||
case kTfLiteBool:
|
||||
EvalUnquantized<bool>(context, node);
|
||||
break;
|
||||
|
||||
default:
|
||||
MicroPrintf("Op Concatenation does not currently support Type '%s'.",
|
||||
TfLiteTypeGetName(output_type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace concatenation
|
||||
|
||||
TfLiteRegistration Register_CONCATENATION() {
|
||||
return tflite::micro::RegisterOp(concatenation::Init, concatenation::Prepare,
|
||||
concatenation::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,174 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/cumsum.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kAxisTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
constexpr int kCumSumIntegerShift = 20;
|
||||
|
||||
// only used with INT8 tensors
|
||||
struct OpData {
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
int32_t input_offset;
|
||||
int32_t output_offset;
|
||||
int32_t input_multiplier;
|
||||
int32_t output_multiplier;
|
||||
int input_shift;
|
||||
int output_shift;
|
||||
int left_shift;
|
||||
};
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TfLiteTensor* axis =
|
||||
micro_context->AllocateTempInputTensor(node, kAxisTensor);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
input->type == kTfLiteFloat32 || input->type == kTfLiteInt8);
|
||||
TF_LITE_ENSURE_EQ(context, axis->type, kTfLiteInt32);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumElements(axis), 1);
|
||||
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) >= 1);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
TF_LITE_ENSURE(context, HaveSameShapes(input, output));
|
||||
|
||||
if (output->type == kTfLiteInt8) {
|
||||
node->user_data =
|
||||
context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
// 8bit -> 8bit general quantized path, with general rescalings
|
||||
data->input_offset = -input->params.zero_point;
|
||||
data->output_offset = output->params.zero_point;
|
||||
data->left_shift = kCumSumIntegerShift;
|
||||
const double twice_max_input_scale =
|
||||
2 * static_cast<double>(input->params.scale);
|
||||
const double real_input_multiplier =
|
||||
static_cast<double>(input->params.scale) / twice_max_input_scale;
|
||||
const double real_output_multiplier =
|
||||
twice_max_input_scale /
|
||||
((1 << data->left_shift) * static_cast<double>(output->params.scale));
|
||||
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
real_input_multiplier, &data->input_multiplier, &data->input_shift);
|
||||
|
||||
QuantizeMultiplierSmallerThanOneExp(
|
||||
real_output_multiplier, &data->output_multiplier, &data->output_shift);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized(
|
||||
context, kTfLiteActNone, output, &data->output_activation_min,
|
||||
&data->output_activation_max));
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(axis);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* axis_tensor =
|
||||
tflite::micro::GetEvalInput(context, node, kAxisTensor);
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
auto* cs_params = static_cast<TfLiteCumsumParams*>(node->builtin_data);
|
||||
auto input_shape = tflite::micro::GetTensorShape(input);
|
||||
|
||||
int32_t axis = *tflite::micro::GetTensorData<int32_t>(axis_tensor);
|
||||
if (axis < 0) axis += input_shape.DimensionsCount();
|
||||
|
||||
if (axis < 0 || axis >= input_shape.DimensionsCount()) {
|
||||
MicroPrintf("CUMSUM Invalid axis: %d", axis);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
reference_ops::CumSum(tflite::micro::GetTensorData<float>(input),
|
||||
input_shape, axis, cs_params->exclusive,
|
||||
cs_params->reverse,
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
|
||||
case kTfLiteInt8: {
|
||||
auto* data = static_cast<OpData*>(node->user_data);
|
||||
ArithmeticParams params;
|
||||
params.left_shift = data->left_shift;
|
||||
params.input1_offset = data->input_offset;
|
||||
params.input1_multiplier = data->input_multiplier;
|
||||
params.input1_shift = data->input_shift;
|
||||
params.output_offset = data->output_offset;
|
||||
params.output_multiplier = data->output_multiplier;
|
||||
params.output_shift = data->output_shift;
|
||||
SetActivationParams(data->output_activation_min,
|
||||
data->output_activation_max, ¶ms);
|
||||
reference_ops::CumSum(params, tflite::micro::GetTensorData<int8_t>(input),
|
||||
input_shape, axis, cs_params->exclusive,
|
||||
cs_params->reverse,
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
MicroPrintf("CUMSUM only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_CUMSUM() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,141 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/depth_to_space.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// input/output tensor shape rank associations
|
||||
constexpr int kBatchRank = 0;
|
||||
constexpr int kHeightRank = 1;
|
||||
constexpr int kWidthRank = 2;
|
||||
constexpr int kDepthRank = 3;
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteDepthToSpaceParams*>(node->builtin_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
|
||||
|
||||
auto data_type = output->type;
|
||||
TF_LITE_ENSURE(context,
|
||||
data_type == kTfLiteFloat32 || data_type == kTfLiteInt8);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
const int block_size = params->block_size;
|
||||
TF_LITE_ENSURE(context, block_size > 0);
|
||||
const int input_height = input->dims->data[kHeightRank];
|
||||
const int input_width = input->dims->data[kWidthRank];
|
||||
const int input_channels = input->dims->data[kDepthRank];
|
||||
int output_height = input_height * block_size;
|
||||
int output_width = input_width * block_size;
|
||||
int output_channels = input_channels / block_size / block_size;
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input_height, output_height / block_size);
|
||||
TF_LITE_ENSURE_EQ(context, input_width, output_width / block_size);
|
||||
TF_LITE_ENSURE_EQ(context, input_channels,
|
||||
output_channels * block_size * block_size);
|
||||
|
||||
// We must update the output tensor dimensions.
|
||||
// The dims storage is expected to be the same area in memory
|
||||
// for both TfLiteTensor and TfLiteEvalTensor. This is important
|
||||
// because TfLiteTensor in the MicroInterpreter is a temporary
|
||||
// allocation. For the KernelRunner interpreter, TfLiteEvalTensor
|
||||
// is a temporary allocation. We must therefore relocate the dims
|
||||
// from the FlatBuffer to the persistant storage arena.
|
||||
TfLiteEvalTensor* output_eval =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
TF_LITE_ENSURE_OK(context, tflite::micro::CreateWritableTensorDimsWithCopy(
|
||||
context, output, output_eval));
|
||||
output->dims->data[kBatchRank] = input->dims->data[kBatchRank];
|
||||
output->dims->data[kHeightRank] = output_height;
|
||||
output->dims->data[kWidthRank] = output_width;
|
||||
output->dims->data[kDepthRank] = output_channels;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteDepthToSpaceParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
tflite::DepthToSpaceParams op_params;
|
||||
op_params.block_size = static_cast<int32_t>(params->block_size);
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::DepthToSpace(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::DepthToSpace(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("DEPTH_TO_SPACE only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DEPTH_TO_SPACE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,98 +0,0 @@
|
||||
/* Copyright 2017 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/micro/kernels/depthwise_conv.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataConv));
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
auto& params =
|
||||
*(reinterpret_cast<TfLiteDepthwiseConvParams*>(node->builtin_data));
|
||||
const OpDataConv& data = *(static_cast<const OpDataConv*>(node->user_data));
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kDepthwiseConvOutputTensor);
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kDepthwiseConvInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
tflite::micro::GetEvalInput(context, node, kDepthwiseConvWeightsTensor);
|
||||
const TfLiteEvalTensor* bias =
|
||||
(NumInputs(node) == 3)
|
||||
? tflite::micro::GetEvalInput(context, node, kDepthwiseConvBiasTensor)
|
||||
: nullptr;
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32: {
|
||||
tflite::reference_ops::DepthwiseConv(
|
||||
DepthwiseConvParamsFloat(params, data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
reference_integer_ops::DepthwiseConvPerChannel(
|
||||
DepthwiseConvParamsQuantized(params, data),
|
||||
data.per_channel_output_multiplier, data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D() {
|
||||
return tflite::micro::RegisterOp(Init, DepthwiseConvPrepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,207 +0,0 @@
|
||||
/* Copyright 2022 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/kernels/internal/reference/div.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor1 = 0;
|
||||
constexpr int kInputTensor2 = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpDataDiv {
|
||||
// Parameters used in the quantized paths where the output is 8bit
|
||||
int32_t input1_zero_point;
|
||||
int32_t input2_zero_point;
|
||||
int32_t output_zero_point;
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
|
||||
// Parameters used in all quantized paths
|
||||
int32_t output_multiplier;
|
||||
int output_shift;
|
||||
};
|
||||
|
||||
TfLiteStatus CalculateOpDataDiv(TfLiteContext* context, TfLiteTensor* input1,
|
||||
TfLiteTensor* input2, TfLiteTensor* output,
|
||||
TfLiteDivParams* params, OpDataDiv* data) {
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, input2->type);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, output->type);
|
||||
|
||||
if (output->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_STATUS(CalculateActivationRangeQuantized(
|
||||
context, params->activation, output, &data->output_activation_min,
|
||||
&data->output_activation_max));
|
||||
const double real_multiplier = static_cast<double>(
|
||||
input1->params.scale / (input2->params.scale * output->params.scale));
|
||||
QuantizeMultiplier(real_multiplier, &data->output_multiplier,
|
||||
&data->output_shift);
|
||||
data->input1_zero_point = input1->params.zero_point;
|
||||
data->input2_zero_point = input2->params.zero_point;
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataDiv));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input1 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor1);
|
||||
TF_LITE_ENSURE(context, input1 != nullptr);
|
||||
TfLiteTensor* input2 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor2);
|
||||
TF_LITE_ENSURE(context, input2 != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
OpDataDiv* data = static_cast<OpDataDiv*>(node->user_data);
|
||||
auto* params = reinterpret_cast<TfLiteDivParams*>(node->builtin_data);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
CalculateOpDataDiv(context, input1, input2, output, params, data));
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input1);
|
||||
micro_context->DeallocateTempTfLiteTensor(input2);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void EvalDiv(TfLiteContext* context, TfLiteNode* node, TfLiteDivParams* params,
|
||||
const OpDataDiv* data, const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2, TfLiteEvalTensor* output) {
|
||||
tflite::ArithmeticParams op_params = {};
|
||||
|
||||
#define TF_LITE_DIV(type, opname, data_type) \
|
||||
data_type output_activation_min, output_activation_max; \
|
||||
CalculateActivationRange(params->activation, &output_activation_min, \
|
||||
&output_activation_max); \
|
||||
SetActivationParams(output_activation_min, output_activation_max, \
|
||||
&op_params); \
|
||||
type::opname(op_params, tflite::micro::GetTensorShape(input1), \
|
||||
tflite::micro::GetTensorData<data_type>(input1), \
|
||||
tflite::micro::GetTensorShape(input2), \
|
||||
tflite::micro::GetTensorData<data_type>(input2), \
|
||||
tflite::micro::GetTensorShape(output), \
|
||||
tflite::micro::GetTensorData<data_type>(output))
|
||||
|
||||
bool requires_broadcast = reference_ops::ProcessBroadcastShapes(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorShape(input2), &op_params);
|
||||
|
||||
if (requires_broadcast) {
|
||||
TF_LITE_DIV(reference_ops, BroadcastDivSlow, float);
|
||||
} else {
|
||||
TF_LITE_DIV(reference_ops, Div, float);
|
||||
}
|
||||
#undef TF_LITE_DIV
|
||||
}
|
||||
|
||||
TfLiteStatus EvalQuantized(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteDivParams* params, const OpDataDiv* data,
|
||||
const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output) {
|
||||
tflite::ArithmeticParams op_params = {};
|
||||
|
||||
#define TF_LITE_DIV(type, opname, dtype) \
|
||||
type::opname(op_params, tflite::micro::GetTensorShape(input1), \
|
||||
tflite::micro::GetTensorData<dtype>(input1), \
|
||||
tflite::micro::GetTensorShape(input2), \
|
||||
tflite::micro::GetTensorData<dtype>(input2), \
|
||||
tflite::micro::GetTensorShape(output), \
|
||||
tflite::micro::GetTensorData<dtype>(output))
|
||||
|
||||
if (input1->type == kTfLiteInt8 && input2->type == kTfLiteInt8 &&
|
||||
output->type == kTfLiteInt8) {
|
||||
SetActivationParams(data->output_activation_min,
|
||||
data->output_activation_max, &op_params);
|
||||
op_params.input1_offset = -data->input1_zero_point;
|
||||
op_params.input2_offset = -data->input2_zero_point;
|
||||
op_params.output_offset = data->output_zero_point;
|
||||
op_params.output_multiplier = data->output_multiplier;
|
||||
op_params.output_shift = data->output_shift;
|
||||
|
||||
bool requires_broadcast = reference_ops::ProcessBroadcastShapes(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorShape(input2), &op_params);
|
||||
|
||||
if (requires_broadcast) {
|
||||
TF_LITE_DIV(reference_ops, BroadcastDivSlow, int8_t);
|
||||
} else {
|
||||
TF_LITE_DIV(reference_ops, Div, int8_t);
|
||||
}
|
||||
#undef TF_LITE_DIV
|
||||
} else {
|
||||
MicroPrintf("Unsupported combination of input and output types in DIV.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
auto* params = static_cast<TfLiteDivParams*>(node->builtin_data);
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
auto* data = static_cast<OpDataDiv*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
EvalDiv(context, node, params, data, input1, input2, output);
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_OK(context, EvalQuantized(context, node, params, data,
|
||||
input1, input2, output));
|
||||
} else {
|
||||
MicroPrintf(
|
||||
"DIV only supports FLOAT32, quantized INT8 "
|
||||
"now, got type %s (%d).",
|
||||
TfLiteTypeGetName(output->type), output->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DIV() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,429 +0,0 @@
|
||||
/* Copyright 2022 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 <cmath>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace elementwise {
|
||||
namespace {
|
||||
|
||||
constexpr int kAbsNameId = 0;
|
||||
constexpr int kRsrqtNameId = 1;
|
||||
|
||||
const int kElementwiseInputTensor = 0;
|
||||
const int kElementwiseOutputTensor = 0;
|
||||
|
||||
struct OpDataAbsRsqrt {
|
||||
int32_t multiplier;
|
||||
int shift;
|
||||
int input_offset;
|
||||
int output_offset;
|
||||
bool needs_rescale;
|
||||
TfLiteQuantizationType input_quantization_type;
|
||||
TfLiteType input_type;
|
||||
};
|
||||
|
||||
bool IsNumericSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32;
|
||||
}
|
||||
|
||||
bool IsLogicalSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteBool;
|
||||
}
|
||||
|
||||
bool IsAbsSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32 || type == kTfLiteInt8 || type == kTfLiteInt16;
|
||||
}
|
||||
|
||||
bool IsRsqrtSupportedType(const TfLiteType type) {
|
||||
return type == kTfLiteFloat32 || type == kTfLiteInt8;
|
||||
}
|
||||
|
||||
inline void SetAbsOutputMultiplier(const float input_scale,
|
||||
const float output_scale,
|
||||
int32_t* multiplier, int* shift) {
|
||||
QuantizeMultiplier(static_cast<double>(input_scale / output_scale),
|
||||
multiplier, shift);
|
||||
}
|
||||
|
||||
inline void SetRsqrtOutputMultiplier(const float input_scale,
|
||||
const float output_scale,
|
||||
int32_t* multiplier, int* shift) {
|
||||
const double scale =
|
||||
1. / static_cast<double>((std::sqrt(input_scale) * output_scale));
|
||||
QuantizeMultiplier(scale, multiplier, shift);
|
||||
}
|
||||
|
||||
typedef bool (*IsSupportedType)(TfLiteType);
|
||||
template <IsSupportedType>
|
||||
TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kElementwiseInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kElementwiseOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
if (!IsSupportedType(input->type)) {
|
||||
MicroPrintf("Input data type %s (%d) is not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
typedef bool (*IsSupportedType)(TfLiteType);
|
||||
template <IsSupportedType, const int op_nameid>
|
||||
TfLiteStatus PrepareAbsRsqrt(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
if (!IsSupportedType(input->type)) {
|
||||
MicroPrintf("Input data type %s (%d) is not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
auto* op_data = static_cast<OpDataAbsRsqrt*>(node->user_data);
|
||||
op_data->input_type = input->type;
|
||||
|
||||
// For int16 type input, we support both quantized and non-quantized
|
||||
// evaluation.
|
||||
if (op_nameid == kAbsNameId) {
|
||||
op_data->input_quantization_type = input->quantization.type;
|
||||
}
|
||||
|
||||
if (input->type == kTfLiteInt8 ||
|
||||
(input->type == kTfLiteInt16 &&
|
||||
input->quantization.type != kTfLiteNoQuantization)) {
|
||||
TF_LITE_ENSURE_EQ(context, input->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
TF_LITE_ENSURE_EQ(context, output->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
const auto* input_params =
|
||||
reinterpret_cast<TfLiteAffineQuantization*>(input->quantization.params);
|
||||
const auto* output_params = reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
output->quantization.params);
|
||||
TF_LITE_ENSURE(context, input_params != nullptr);
|
||||
TF_LITE_ENSURE(context, input_params->scale != nullptr);
|
||||
TF_LITE_ENSURE(context, input_params->scale->size > 0);
|
||||
TF_LITE_ENSURE(context, input_params->zero_point->size > 0);
|
||||
TF_LITE_ENSURE(context, output_params != nullptr);
|
||||
TF_LITE_ENSURE(context, output_params->scale != nullptr);
|
||||
TF_LITE_ENSURE(context, output_params->scale->size > 0);
|
||||
TF_LITE_ENSURE(context, output_params->zero_point->size > 0);
|
||||
op_data->input_offset = input_params->zero_point->data[0];
|
||||
op_data->output_offset = output_params->zero_point->data[0];
|
||||
if (input->type == kTfLiteInt16) {
|
||||
TF_LITE_ENSURE_EQ(context, op_data->input_offset, 0);
|
||||
TF_LITE_ENSURE_EQ(context, op_data->output_offset, 0);
|
||||
}
|
||||
const float input_scale = input_params->scale->data[0];
|
||||
const float output_scale = output_params->scale->data[0];
|
||||
op_data->needs_rescale = input_scale != output_scale;
|
||||
if (op_nameid == kAbsNameId && op_data->needs_rescale) {
|
||||
SetAbsOutputMultiplier(input_scale, output_scale, &op_data->multiplier,
|
||||
&op_data->shift);
|
||||
} else if (op_nameid == kRsrqtNameId) {
|
||||
SetRsqrtOutputMultiplier(input_scale, output_scale, &op_data->multiplier,
|
||||
&op_data->shift);
|
||||
}
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TfLiteStatus EvalImplQuantized(
|
||||
TfLiteContext* context, TfLiteNode* node,
|
||||
T func(TfLiteContext*, TfLiteNode*, T),
|
||||
TfLiteStatus validate_input_func(TfLiteContext*, TfLiteNode*, T),
|
||||
TfLiteType expected_type) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
|
||||
const size_t num_elements = ElementCount(*input->dims);
|
||||
const T* in_data = tflite::micro::GetTensorData<T>(input);
|
||||
T* out_data = tflite::micro::GetTensorData<T>(output);
|
||||
for (size_t i = 0; i < num_elements; ++i) {
|
||||
if (validate_input_func) {
|
||||
TF_LITE_ENSURE_OK(context,
|
||||
validate_input_func(context, node, in_data[i]));
|
||||
}
|
||||
out_data[i] = func(context, node, in_data[i]);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T AbsHelper(T i) {
|
||||
return std::abs(i);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
T func(T), TfLiteStatus validate_input_func(T),
|
||||
TfLiteType expected_type) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
|
||||
const size_t num_elements = ElementCount(*input->dims);
|
||||
const T* in_data = tflite::micro::GetTensorData<T>(input);
|
||||
T* out_data = tflite::micro::GetTensorData<T>(output);
|
||||
for (size_t i = 0; i < num_elements; ++i) {
|
||||
if (validate_input_func) {
|
||||
TF_LITE_ENSURE_OK(context, validate_input_func(in_data[i]));
|
||||
}
|
||||
out_data[i] = func(in_data[i]);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
inline TfLiteStatus EvalNumeric(TfLiteContext* context, TfLiteNode* node,
|
||||
float float_func(float)) {
|
||||
return EvalImpl<float>(context, node, float_func,
|
||||
/*validate_input_func=*/nullptr, kTfLiteFloat32);
|
||||
}
|
||||
|
||||
inline TfLiteStatus EvalLogical(TfLiteContext* context, TfLiteNode* node,
|
||||
|
||||
bool bool_func(bool)) {
|
||||
return EvalImpl<bool>(context, node, bool_func,
|
||||
/*validate_input_func=*/nullptr, kTfLiteBool);
|
||||
}
|
||||
|
||||
void* ElementWiseAbsRsqrtInit(TfLiteContext* context, const char* buffer,
|
||||
size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataAbsRsqrt));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T AbsEvalQuantized(TfLiteContext* context, TfLiteNode* node, T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
const int kMin = std::numeric_limits<T>::min();
|
||||
const int kMax = std::numeric_limits<T>::max();
|
||||
|
||||
const int32_t value = std::abs(i - op_data->input_offset);
|
||||
if (!op_data->needs_rescale) {
|
||||
return static_cast<T>(
|
||||
std::min(std::max(static_cast<long int>(value + op_data->output_offset),
|
||||
static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
const int32_t output = tflite::MultiplyByQuantizedMultiplier(
|
||||
value, op_data->multiplier, op_data->shift) +
|
||||
op_data->output_offset;
|
||||
return static_cast<T>(std::min(
|
||||
std::max(static_cast<long int>(output), static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T RsqrtEvalQuantized(TfLiteContext* context, TfLiteNode* node, T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
const int kMin = std::numeric_limits<T>::min();
|
||||
const int kMax = std::numeric_limits<T>::max();
|
||||
|
||||
const int32_t value = (i - op_data->input_offset);
|
||||
const int32_t kShift = 20; // Shift to keep value integer.
|
||||
if (value == 0) {
|
||||
// Assume that any value close to 0 represents the max output value.
|
||||
return static_cast<T>(kMax);
|
||||
}
|
||||
int32_t inv_sqrt_multiplier;
|
||||
int inv_sqrt_shift;
|
||||
GetInvSqrtQuantizedMultiplierExp(value, kReverseShift, &inv_sqrt_multiplier,
|
||||
&inv_sqrt_shift);
|
||||
const int32_t data = tflite::MultiplyByQuantizedMultiplier(
|
||||
static_cast<int32_t>(1), inv_sqrt_multiplier, inv_sqrt_shift + kShift);
|
||||
const int32_t output =
|
||||
tflite::MultiplyByQuantizedMultiplier(data, op_data->multiplier,
|
||||
op_data->shift - kShift) +
|
||||
op_data->output_offset;
|
||||
return static_cast<T>(std::min(
|
||||
std::max(static_cast<long int>(output), static_cast<long int>(kMin)),
|
||||
static_cast<long int>(kMax)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus validate_input_func(TfLiteContext* context, TfLiteNode* node,
|
||||
T i) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE_MSG(context, i >= op_data->input_offset,
|
||||
"Rsqrt is only defined for positive values");
|
||||
return static_cast<TfLiteStatus>(kTfLiteOk);
|
||||
}
|
||||
|
||||
TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpDataAbsRsqrt* op_data = reinterpret_cast<OpDataAbsRsqrt*>(node->user_data);
|
||||
TfLiteType type = op_data->input_type;
|
||||
TfLiteQuantizationType input_quantization_type =
|
||||
op_data->input_quantization_type;
|
||||
TfLiteStatus eval_result;
|
||||
|
||||
switch (type) {
|
||||
case kTfLiteFloat32:
|
||||
eval_result = EvalNumeric(context, node, std::abs);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
eval_result =
|
||||
EvalImplQuantized<int8_t>(context, node, AbsEvalQuantized,
|
||||
/*validate_input_func=*/nullptr, type);
|
||||
break;
|
||||
case kTfLiteInt16:
|
||||
eval_result =
|
||||
input_quantization_type == kTfLiteNoQuantization
|
||||
? EvalImpl<int16_t>(context, node, AbsHelper,
|
||||
/*validate_input_func=*/nullptr, type)
|
||||
: EvalImplQuantized<int16_t>(context, node, AbsEvalQuantized,
|
||||
/*validate_input_func=*/nullptr,
|
||||
type);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
return eval_result;
|
||||
}
|
||||
|
||||
TfLiteStatus SinEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalNumeric(context, node, std::sin);
|
||||
}
|
||||
|
||||
TfLiteStatus CosEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalNumeric(context, node, std::cos);
|
||||
}
|
||||
|
||||
TfLiteStatus LogEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalNumeric(context, node, std::log);
|
||||
}
|
||||
|
||||
TfLiteStatus SqrtEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalNumeric(context, node, std::sqrt);
|
||||
}
|
||||
|
||||
TfLiteStatus RsqrtEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const auto* op_data = static_cast<const OpDataAbsRsqrt*>(node->user_data);
|
||||
TfLiteType type = op_data->input_type;
|
||||
switch (type) {
|
||||
case kTfLiteFloat32:
|
||||
return EvalImpl<float>(
|
||||
context, node, [](float f) { return 1.f / std::sqrt(f); },
|
||||
/*validate_input_func=*/nullptr, type);
|
||||
case kTfLiteInt8:
|
||||
return EvalImplQuantized<int8_t>(context, node,
|
||||
elementwise::RsqrtEvalQuantized,
|
||||
elementwise::validate_input_func, type);
|
||||
|
||||
default:
|
||||
MicroPrintf("Current data type %s is not supported.",
|
||||
TfLiteTypeGetName(type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus SquareEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalNumeric(context, node, [](float f) { return f * f; });
|
||||
}
|
||||
|
||||
TfLiteStatus LogicalNotEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
return EvalLogical(context, node, [](bool v) { return !v; });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace elementwise
|
||||
|
||||
TfLiteRegistration Register_ABS() {
|
||||
return tflite::micro::RegisterOp(
|
||||
elementwise::ElementWiseAbsRsqrtInit,
|
||||
elementwise::PrepareAbsRsqrt<elementwise::IsAbsSupportedType,
|
||||
elementwise::kAbsNameId>,
|
||||
elementwise::AbsEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SIN() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SinEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_COS() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::CosEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LOG() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::LogEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SQRT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SqrtEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_RSQRT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
elementwise::ElementWiseAbsRsqrtInit,
|
||||
elementwise::PrepareAbsRsqrt<elementwise::IsRsqrtSupportedType,
|
||||
elementwise::kRsrqtNameId>,
|
||||
elementwise::RsqrtEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_SQUARE() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsNumericSupportedType>,
|
||||
elementwise::SquareEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_LOGICAL_NOT() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, elementwise::GenericPrepare<elementwise::IsLogicalSupportedType>,
|
||||
elementwise::LogicalNotEval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,150 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/elu.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/cppmath.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// Input/output tensor index.
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// OLD-TODO(b/142762739): We should figure out a multi-threading plan for most
|
||||
// of the activation ops below.
|
||||
|
||||
struct OpData {
|
||||
int8_t table[256];
|
||||
};
|
||||
|
||||
using TransformFunc = float (*)(float);
|
||||
|
||||
template <typename T>
|
||||
void PopulateLookupTable(const TfLiteTensor* input, const TfLiteTensor* output,
|
||||
const TransformFunc transform, OpData* data) {
|
||||
if (sizeof(T) != 1) {
|
||||
MicroPrintf("Lookup table valid only for 8bit");
|
||||
TFLITE_ABORT;
|
||||
}
|
||||
|
||||
const float inverse_scale = 1 / output->params.scale;
|
||||
int32_t maxval = std::numeric_limits<T>::max();
|
||||
int32_t minval = std::numeric_limits<T>::min();
|
||||
for (int32_t val = minval; val <= maxval; ++val) {
|
||||
const float dequantized =
|
||||
input->params.scale * (val - input->params.zero_point);
|
||||
const float transformed = transform(dequantized);
|
||||
const float rescaled = TfLiteRound(transformed * inverse_scale);
|
||||
const int32_t quantized =
|
||||
static_cast<int32_t>(rescaled + output->params.zero_point);
|
||||
data->table[static_cast<uint8_t>(static_cast<T>(val))] =
|
||||
static_cast<T>(std::max(std::min(maxval, quantized), minval));
|
||||
}
|
||||
}
|
||||
|
||||
// OLD-TODO(b/143696793): move this to optimized_ops.
|
||||
void EvalUsingLookupTable(const OpData* data, const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output) {
|
||||
const int size = MatchingFlatSize(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorShape(output));
|
||||
int8_t* output_data = tflite::micro::GetTensorData<int8_t>(output);
|
||||
const int8_t* input_data = tflite::micro::GetTensorData<int8_t>(input);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
output_data[i] = data->table[static_cast<uint8_t>(input_data[i])];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
// Use LUT to handle quantized elu path.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
TransformFunc transform = [](float value) {
|
||||
return value < 0.0f ? std::exp(value) - 1.0f : value;
|
||||
};
|
||||
PopulateLookupTable<int8_t>(input, output, transform, data);
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* EluInit(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
// This is a builtin op, so we don't use the contents in 'buffer', if any.
|
||||
// Instead, we allocate a new object to carry information from Prepare() to
|
||||
// Eval().
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus EluPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
TfLiteStatus EluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
reference_ops::Elu(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
const OpData* data = static_cast<OpData*>(node->user_data);
|
||||
EvalUsingLookupTable(data, input, output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("ELU only supports float32 and int8 currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_ELU() {
|
||||
return tflite::micro::RegisterOp(EluInit, EluPrepare, EluEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,343 +0,0 @@
|
||||
/* 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/micro/kernels/conv.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/conv.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/conv.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
#include <esp_nn.h>
|
||||
#endif
|
||||
|
||||
|
||||
long long conv_total_time = 0;
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
struct NodeData {
|
||||
OpDataConv op_data;
|
||||
#if ESP_NN
|
||||
int buffer_idx;
|
||||
#endif
|
||||
};
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(NodeData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
NodeData* data = static_cast<NodeData*>(node->user_data);
|
||||
const auto& params =
|
||||
*(static_cast<const TfLiteConvParams*>(node->builtin_data));
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kConvInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* filter =
|
||||
micro_context->AllocateTempInputTensor(node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
const int input_width = input->dims->data[2];
|
||||
const int input_height = input->dims->data[1];
|
||||
const int filter_width = filter->dims->data[2];
|
||||
const int filter_height = filter->dims->data[1];
|
||||
const int output_width = output->dims->data[2];
|
||||
const int output_height = output->dims->data[1];
|
||||
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
data->op_data.per_channel_output_multiplier =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
data->op_data.per_channel_output_shift =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
|
||||
// All per-channel quantized tensors need valid zero point and scale arrays.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_EQ(context, filter->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
|
||||
const auto* affine_quantization =
|
||||
static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
|
||||
TFLITE_DCHECK(affine_quantization != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->scale != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->zero_point != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
affine_quantization->scale->size == 1 ||
|
||||
affine_quantization->scale->size ==
|
||||
filter->dims->data[kConvQuantizedDimension]);
|
||||
TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
|
||||
affine_quantization->zero_point->size);
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateOpDataConv(
|
||||
context, node, params, input_width, input_height, filter_width,
|
||||
filter_height, output_width, output_height, input->type, &data->op_data));
|
||||
|
||||
#if ESP_NN
|
||||
if (input->type == kTfLiteInt8) {
|
||||
data_dims_t input_dims = {
|
||||
.width = input_width, .height = input_height,
|
||||
.channels = input->dims->data[3], 1
|
||||
};
|
||||
data_dims_t output_dims = {
|
||||
.width = output_width, .height = output_height,
|
||||
.channels = output->dims->data[3], 1
|
||||
};
|
||||
data_dims_t filter_dims = {.width = filter_width, .height = filter_height, 0, 0};
|
||||
conv_params_t conv_params = {
|
||||
.in_offset = 0, .out_offset = 0,
|
||||
.stride = {params.stride_width, params.stride_height},
|
||||
.padding = {data->op_data.padding.width, data->op_data.padding.height},
|
||||
.dilation = {0, 0}, .activation = {-128, 127}
|
||||
};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_conv_scratch_size(
|
||||
&input_dims, &filter_dims, &output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
TF_LITE_ENSURE_STATUS(context->RequestScratchBufferInArena(
|
||||
context, scratch_buf_size, &data->buffer_idx));
|
||||
} else {
|
||||
data->buffer_idx = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(filter);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
#if ESP_NN
|
||||
// Fixed-point per-channel-quantization convolution Int8 function wrapper.
|
||||
inline void EvalQuantizedPerChannel(
|
||||
TfLiteContext* context, TfLiteNode* node, const TfLiteConvParams& params,
|
||||
const NodeData& data, const TfLiteEvalTensor* input,
|
||||
const TfLiteEvalTensor* filter, const TfLiteEvalTensor* bias,
|
||||
TfLiteEvalTensor* output) {
|
||||
const int dilation_width_factor = params.dilation_width_factor;
|
||||
const int dilation_height_factor = params.dilation_height_factor;
|
||||
|
||||
if (dilation_width_factor == 1 && dilation_height_factor == 1) {
|
||||
// Get parameters.
|
||||
RuntimeShape filter_shape = tflite::micro::GetTensorShape(filter);
|
||||
RuntimeShape input_shape = tflite::micro::GetTensorShape(input);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
RuntimeShape bias_shape = tflite::micro::GetTensorShape(bias);
|
||||
|
||||
const int8_t *input_data = tflite::micro::GetTensorData<int8_t>(input);
|
||||
int8_t *output_data = tflite::micro::GetTensorData<int8_t>(output);
|
||||
|
||||
const int32_t input_offset = -data.op_data.input_zero_point;
|
||||
const int32_t output_offset = data.op_data.output_zero_point;
|
||||
const int stride_width = params.stride_width;
|
||||
const int stride_height = params.stride_height;
|
||||
const int pad_width = data.op_data.padding.width;
|
||||
const int pad_height = data.op_data.padding.height;
|
||||
|
||||
const int input_height = input_shape.Dims(1);
|
||||
const int input_width = input_shape.Dims(2);
|
||||
const int filter_height = filter_shape.Dims(1);
|
||||
const int filter_width = filter_shape.Dims(2);
|
||||
const int output_height = output_shape.Dims(1);
|
||||
const int output_width = output_shape.Dims(2);
|
||||
|
||||
// Set min and max value of the output.
|
||||
const int32_t activation_min = data.op_data.output_activation_min;
|
||||
const int32_t activation_max = data.op_data.output_activation_max;
|
||||
|
||||
// Consistency check.
|
||||
TFLITE_DCHECK_LE(activation_min, activation_max);
|
||||
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
|
||||
const int batch_size = MatchingDim(input_shape, 0, output_shape, 0);
|
||||
const int input_depth = MatchingDim(input_shape, 3, filter_shape, 3);
|
||||
const int output_depth = MatchingDim(filter_shape, 0, output_shape, 3);
|
||||
|
||||
if (tflite::micro::GetTensorData<int8_t>(bias)) {
|
||||
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
|
||||
}
|
||||
|
||||
void *scratch_buf = NULL;
|
||||
if (data.buffer_idx > -1) {
|
||||
scratch_buf = context->GetScratchBuffer(context, data.buffer_idx);
|
||||
}
|
||||
esp_nn_set_conv_scratch_buf(scratch_buf);
|
||||
|
||||
const int input_size = input_width * input_height * input_depth;
|
||||
const int output_size = output_width * output_height * output_depth;
|
||||
|
||||
data_dims_t input_dims = {
|
||||
.width = input_width, .height = input_height,
|
||||
.channels = input_depth, 1
|
||||
};
|
||||
data_dims_t output_dims = {
|
||||
.width = output_width, .height = output_height,
|
||||
.channels = output_depth, 1
|
||||
};
|
||||
data_dims_t filter_dims = {.width = filter_width, .height = filter_height, 0, 0};
|
||||
conv_params_t conv_params = {
|
||||
.in_offset = input_offset, .out_offset = output_offset,
|
||||
.stride = {stride_width, stride_height},
|
||||
.padding = {pad_width, pad_height},
|
||||
.dilation = {0, 0},
|
||||
.activation = {activation_min, activation_max}
|
||||
};
|
||||
quant_data_t quant_data = {
|
||||
.shift = data.op_data.per_channel_output_shift,
|
||||
.mult = data.op_data.per_channel_output_multiplier
|
||||
};
|
||||
|
||||
for (int i_batch = 0; i_batch < batch_size; i_batch++) {
|
||||
esp_nn_conv_s8(&input_dims, input_data + i_batch * input_size,
|
||||
&filter_dims, tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
&output_dims, output_data + i_batch * output_size,
|
||||
&conv_params, &quant_data);
|
||||
}
|
||||
} else {
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data.op_data),
|
||||
data.op_data.per_channel_output_multiplier,
|
||||
data.op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kConvInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
tflite::micro::GetEvalInput(context, node, kConvWeightsTensor);
|
||||
const TfLiteEvalTensor* bias =
|
||||
(NumInputs(node) == 3)
|
||||
? tflite::micro::GetEvalInput(context, node, kConvBiasTensor)
|
||||
: nullptr;
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kConvOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
const auto& params =
|
||||
*(reinterpret_cast<TfLiteConvParams*>(node->builtin_data));
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const auto& data = *(static_cast<const NodeData*>(node->user_data));
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
TF_LITE_ENSURE_MSG(context, input->type == filter->type,
|
||||
"Hybrid models are not supported on TFLite Micro.");
|
||||
|
||||
long long start_time = esp_timer_get_time();
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32: {
|
||||
tflite::reference_ops::Conv(
|
||||
ConvParamsFloat(params, data.op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr);
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
#if ESP_NN
|
||||
EvalQuantizedPerChannel(context, node, params, data, input, filter,
|
||||
bias, output);
|
||||
#else
|
||||
reference_integer_ops::ConvPerChannel(
|
||||
ConvParamsQuantized(params, data.op_data),
|
||||
data.op_data.per_channel_output_multiplier,
|
||||
data.op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case kTfLiteUInt8: {
|
||||
//EvalQuantized
|
||||
reference_ops::Conv(ConvParamsQuantized(params, data.op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<uint8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<uint8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<uint8_t>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr,
|
||||
nullptr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
long long time_this_instance = esp_timer_get_time() - start_time;
|
||||
conv_total_time += time_this_instance;
|
||||
//printf("time this instance: %llu\n", time_this_instance / 1000);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_CONV_2D() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,345 +0,0 @@
|
||||
/* Copyright 2017 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/micro/kernels/depthwise_conv.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/depthwiseconv_float.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/depthwiseconv_uint8.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/depthwise_conv.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
#include <esp_nn.h>
|
||||
#endif
|
||||
|
||||
long long dc_total_time = 0;
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
struct NodeData {
|
||||
OpDataConv op_data;
|
||||
#if ESP_NN
|
||||
int buffer_idx;
|
||||
#endif
|
||||
};
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(NodeData));
|
||||
}
|
||||
|
||||
#if ESP_NN
|
||||
inline void EvalQuantizedPerChannel(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteDepthwiseConvParams& params,
|
||||
const NodeData& data,
|
||||
const TfLiteEvalTensor* input,
|
||||
const TfLiteEvalTensor* filter,
|
||||
const TfLiteEvalTensor* bias,
|
||||
TfLiteEvalTensor* output) {
|
||||
const int dilation_width_factor = params.dilation_width_factor;
|
||||
const int dilation_height_factor = params.dilation_height_factor;
|
||||
|
||||
if (dilation_width_factor == 1 && dilation_height_factor == 1) {
|
||||
// Get parameters.
|
||||
RuntimeShape input_shape = tflite::micro::GetTensorShape(input);
|
||||
RuntimeShape filter_shape = tflite::micro::GetTensorShape(filter);
|
||||
RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
RuntimeShape bias_shape = tflite::micro::GetTensorShape(bias);
|
||||
|
||||
TFLITE_DCHECK_EQ(input_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_EQ(filter_shape.DimensionsCount(), 4);
|
||||
TFLITE_DCHECK_EQ(output_shape.DimensionsCount(), 4);
|
||||
|
||||
const int8_t *input_data = tflite::micro::GetTensorData<int8_t>(input);
|
||||
int8_t *output_data = tflite::micro::GetTensorData<int8_t>(output);
|
||||
|
||||
const int depth_multiplier = params.depth_multiplier;
|
||||
const int32_t input_offset = -data.op_data.input_zero_point;
|
||||
const int32_t output_offset = data.op_data.output_zero_point;
|
||||
const int stride_width = params.stride_width;
|
||||
const int stride_height = params.stride_height;
|
||||
const int pad_width = data.op_data.padding.width;
|
||||
const int pad_height = data.op_data.padding.height;
|
||||
|
||||
const int input_height = input_shape.Dims(1);
|
||||
const int input_width = input_shape.Dims(2);
|
||||
const int input_depth = input_shape.Dims(3);
|
||||
const int filter_height = filter_shape.Dims(1);
|
||||
const int filter_width = filter_shape.Dims(2);
|
||||
const int output_height = output_shape.Dims(1);
|
||||
const int output_width = output_shape.Dims(2);
|
||||
|
||||
// Set min and max value of the output.
|
||||
const int32_t activation_min = data.op_data.output_activation_min;
|
||||
const int32_t activation_max = data.op_data.output_activation_max;
|
||||
|
||||
// Consistency check.
|
||||
TFLITE_DCHECK_LE(activation_min, activation_max);
|
||||
const int batch_size = MatchingDim(input_shape, 0, output_shape, 0);
|
||||
const int output_depth = MatchingDim(filter_shape, 3, output_shape, 3);
|
||||
|
||||
TFLITE_DCHECK_EQ(output_depth, input_depth * depth_multiplier);
|
||||
if (tflite::micro::GetTensorData<int8_t>(bias)) {
|
||||
TFLITE_DCHECK_EQ(bias_shape.FlatSize(), output_depth);
|
||||
}
|
||||
|
||||
const int input_size = input_width * input_height * input_depth;
|
||||
const int output_size = output_width * output_height * output_depth;
|
||||
void *scratch_buf = NULL;
|
||||
if (data.buffer_idx > -1) {
|
||||
scratch_buf = context->GetScratchBuffer(context, data.buffer_idx);
|
||||
}
|
||||
|
||||
esp_nn_set_depthwise_conv_scratch_buf(scratch_buf);
|
||||
|
||||
data_dims_t input_dims = {
|
||||
.width = input_width, .height = input_height,
|
||||
.channels = input_depth, 1
|
||||
};
|
||||
data_dims_t output_dims = {
|
||||
.width = output_width, .height = output_height,
|
||||
.channels = output_depth, 1
|
||||
};
|
||||
data_dims_t filter_dims = {.width = filter_width, .height = filter_height, 0, 0};
|
||||
dw_conv_params_t conv_params = {
|
||||
.in_offset = input_offset, .out_offset = output_offset,
|
||||
.ch_mult = depth_multiplier,
|
||||
.stride = {stride_width, stride_height},
|
||||
.padding = {pad_width, pad_height}, .dilation = {0, 0},
|
||||
.activation = {activation_min, activation_max}
|
||||
};
|
||||
quant_data_t quant_data = {
|
||||
.shift = data.op_data.per_channel_output_shift,
|
||||
.mult = data.op_data.per_channel_output_multiplier
|
||||
};
|
||||
|
||||
for (int i_batch = 0; i_batch < batch_size; i_batch++) {
|
||||
esp_nn_depthwise_conv_s8(&input_dims, input_data + i_batch * input_size,
|
||||
&filter_dims, tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
&output_dims, output_data + i_batch * output_size,
|
||||
&conv_params, &quant_data);
|
||||
}
|
||||
} else {
|
||||
reference_integer_ops::DepthwiseConvPerChannel(
|
||||
DepthwiseConvParamsQuantized(params, data.op_data),
|
||||
data.op_data.per_channel_output_multiplier,
|
||||
data.op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
NodeData* data = static_cast<NodeData*>(node->user_data);
|
||||
const TfLiteDepthwiseConvParams& params =
|
||||
*(static_cast<const TfLiteDepthwiseConvParams*>(node->builtin_data));
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kConvInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* filter =
|
||||
micro_context->AllocateTempInputTensor(node, kConvWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
TfLiteTensor* bias =
|
||||
micro_context->AllocateTempInputTensor(node, kConvBiasTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kConvOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
const int input_width = input->dims->data[2];
|
||||
const int input_height = input->dims->data[1];
|
||||
const int filter_width = filter->dims->data[2];
|
||||
const int filter_height = filter->dims->data[1];
|
||||
const int output_width = output->dims->data[2];
|
||||
const int output_height = output->dims->data[1];
|
||||
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
const int num_channels = filter->dims->data[kDepthwiseConvQuantizedDimension];
|
||||
data->op_data.per_channel_output_multiplier =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
data->op_data.per_channel_output_shift =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
|
||||
// All per-channel quantized tensors need valid zero point and scale arrays.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TF_LITE_ENSURE_EQ(context, filter->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
|
||||
const auto* affine_quantization =
|
||||
static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
|
||||
TFLITE_DCHECK(affine_quantization != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->scale != nullptr);
|
||||
TFLITE_DCHECK(affine_quantization->zero_point != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(
|
||||
context, affine_quantization->scale->size == 1 ||
|
||||
affine_quantization->scale->size ==
|
||||
filter->dims->data[kDepthwiseConvQuantizedDimension]);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
|
||||
affine_quantization->zero_point->size);
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateOpDataDepthwiseConv(
|
||||
context, node, params, input_width, input_height, filter_width,
|
||||
filter_height, output_width, output_height, input->type, &data->op_data));
|
||||
|
||||
#if ESP_NN
|
||||
if (input->type == kTfLiteInt8) {
|
||||
data_dims_t input_dims = {
|
||||
.width = input_width, .height = input_height,
|
||||
.channels = input->dims->data[3], 1
|
||||
};
|
||||
data_dims_t output_dims = {
|
||||
.width = output_width, .height = output_height,
|
||||
.channels = output->dims->data[3], 1
|
||||
};
|
||||
data_dims_t filter_dims = {.width = filter_width, .height = filter_height, 0, 0};
|
||||
dw_conv_params_t conv_params = {
|
||||
.in_offset = 0, .out_offset = 0,
|
||||
.ch_mult = params.depth_multiplier,
|
||||
.stride = {params.stride_width, params.stride_height},
|
||||
.padding = {data->op_data.padding.width, data->op_data.padding.height},
|
||||
.dilation = {0, 0}, .activation = {-128, 127}
|
||||
};
|
||||
|
||||
int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(
|
||||
&input_dims, &filter_dims, &output_dims, &conv_params);
|
||||
if (scratch_buf_size > 0) {
|
||||
TF_LITE_ENSURE_STATUS(context->RequestScratchBufferInArena(
|
||||
context, scratch_buf_size, &data->buffer_idx));
|
||||
} else {
|
||||
data->buffer_idx = -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(filter);
|
||||
micro_context->DeallocateTempTfLiteTensor(bias);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
auto& params =
|
||||
*(reinterpret_cast<TfLiteDepthwiseConvParams*>(node->builtin_data));
|
||||
const NodeData& data = *(static_cast<const NodeData*>(node->user_data));
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kDepthwiseConvOutputTensor);
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kDepthwiseConvInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
tflite::micro::GetEvalInput(context, node, kDepthwiseConvWeightsTensor);
|
||||
const TfLiteEvalTensor* bias =
|
||||
(NumInputs(node) == 3)
|
||||
? tflite::micro::GetEvalInput(context, node, kDepthwiseConvBiasTensor)
|
||||
: nullptr;
|
||||
|
||||
long long start_time = esp_timer_get_time();
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
tflite::reference_ops::DepthwiseConv(
|
||||
DepthwiseConvParamsFloat(params, data.op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
#if ESP_NN
|
||||
EvalQuantizedPerChannel(context, node, params, data, input, filter, bias,
|
||||
output);
|
||||
#else
|
||||
reference_integer_ops::DepthwiseConvPerChannel(
|
||||
DepthwiseConvParamsQuantized(params, data.op_data),
|
||||
data.op_data.per_channel_output_multiplier,
|
||||
data.op_data.per_channel_output_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
#endif
|
||||
break;
|
||||
case kTfLiteUInt8:
|
||||
//EvalQuantized(context, node, params, &data, input, filter, bias, output);
|
||||
reference_ops::DepthwiseConv(
|
||||
DepthwiseConvParamsQuantized(params, data.op_data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<uint8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<uint8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<uint8_t>(output));
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
long long time_this_instance = esp_timer_get_time() - start_time;
|
||||
dc_total_time += time_this_instance;
|
||||
// printf("time this instance: %llu\n", time_this_instance / 1000);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,207 +0,0 @@
|
||||
/* Copyright 2021 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/micro/kernels/softmax.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/softmax.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
#include <esp_timer.h>
|
||||
|
||||
#if ESP_NN
|
||||
#include <esp_nn.h>
|
||||
#endif
|
||||
|
||||
long long softmax_total_time = 0;
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
// Softmax parameter data that persists in user_data
|
||||
const int kInt16LUTArraySize = 513;
|
||||
|
||||
struct NodeData {
|
||||
SoftmaxParams op_data;
|
||||
#if ESP_NN
|
||||
int buffer_idx;
|
||||
#endif
|
||||
};
|
||||
|
||||
static void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(NodeData));
|
||||
}
|
||||
|
||||
void SoftmaxQuantized(TfLiteContext* context, const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output, const NodeData* data) {
|
||||
if (input->type == kTfLiteInt8) {
|
||||
if (output->type == kTfLiteInt16) {
|
||||
tflite::reference_ops::Softmax(
|
||||
data->op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
} else {
|
||||
#if ESP_NN
|
||||
const int32_t input_beta_multiplier = data->op_data.input_multiplier;
|
||||
const int32_t input_beta_left_shift = data->op_data.input_left_shift;
|
||||
const int diff_min = data->op_data.diff_min;
|
||||
const RuntimeShape input_shape = tflite::micro::GetTensorShape(input);
|
||||
const RuntimeShape output_shape = tflite::micro::GetTensorShape(output);
|
||||
const int trailing_dim = input_shape.DimensionsCount() - 1;
|
||||
const int outer_size =
|
||||
MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape);
|
||||
const int depth =
|
||||
MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim);
|
||||
const int8_t *in_ptr = tflite::micro::GetTensorData<int8_t>(input);
|
||||
int8_t *out_ptr = tflite::micro::GetTensorData<int8_t>(output);
|
||||
void *scratch_buf = NULL;
|
||||
if (data->buffer_idx > -1) {
|
||||
scratch_buf = context->GetScratchBuffer(context, data->buffer_idx);
|
||||
}
|
||||
esp_nn_set_softmax_scratch_buf(scratch_buf);
|
||||
esp_nn_softmax_s8(in_ptr, outer_size, depth, input_beta_multiplier,
|
||||
input_beta_left_shift, diff_min, out_ptr);
|
||||
#else
|
||||
tflite::reference_ops::Softmax(
|
||||
data->op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
tflite::reference_ops::SoftmaxInt16(
|
||||
data->op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
}
|
||||
}
|
||||
|
||||
static TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
NodeData data = *static_cast<NodeData*>(node->user_data);
|
||||
|
||||
long long start_time = esp_timer_get_time();
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
tflite::reference_ops::Softmax(
|
||||
data.op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
}
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
case kTfLiteInt16: {
|
||||
SoftmaxQuantized(context, input, output, &data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
softmax_total_time += esp_timer_get_time() - start_time;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
static TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) >= 1);
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context, node->user_data != nullptr);
|
||||
NodeData* data = static_cast<NodeData*>(node->user_data);
|
||||
// Only allocate LUTs for KTfLiteInt16 data type
|
||||
if (input->type == kTfLiteInt16) {
|
||||
void* raw_exp_lut = context->AllocatePersistentBuffer(
|
||||
context, sizeof(int16_t) * kInt16LUTArraySize);
|
||||
TF_LITE_ENSURE(context, raw_exp_lut != nullptr);
|
||||
data->op_data.exp_lut = reinterpret_cast<int16_t*>(raw_exp_lut);
|
||||
void* one_over_one_plus_x_lut = context->AllocatePersistentBuffer(
|
||||
context, sizeof(int16_t) * kInt16LUTArraySize);
|
||||
TF_LITE_ENSURE(context, one_over_one_plus_x_lut != nullptr);
|
||||
data->op_data.one_over_one_plus_x_lut =
|
||||
reinterpret_cast<int16_t*>(one_over_one_plus_x_lut);
|
||||
}
|
||||
|
||||
if (output->type == kTfLiteInt16) {
|
||||
TF_LITE_ENSURE(context,
|
||||
input->type == kTfLiteInt8 || input->type == kTfLiteInt16);
|
||||
} else {
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
}
|
||||
|
||||
// Populate LUT if required
|
||||
if (input->type == kTfLiteInt16) {
|
||||
TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
|
||||
// exp LUT only used on negative values
|
||||
// we consider exp(-10.0) is insignificant to accumulation
|
||||
gen_lut<float, int16_t, int16_t>(
|
||||
[](float value) { return std::exp(value); }, -10.0f, 0.0f, -1.0f, 1.0f,
|
||||
data->op_data.exp_lut);
|
||||
gen_lut<float, int16_t, int16_t>(
|
||||
[](float value) { return 1.0f / (1.0f + value); }, 0.0f, 1.0f, -1.0f,
|
||||
1.0f, data->op_data.one_over_one_plus_x_lut);
|
||||
data->op_data.zero_point = output->params.zero_point;
|
||||
data->op_data.scale = output->params.scale;
|
||||
}
|
||||
|
||||
auto* params = static_cast<TfLiteSoftmaxParams*>(node->builtin_data);
|
||||
auto ret_val =
|
||||
CalculateSoftmaxParams(context, input, output, params, &data->op_data);
|
||||
|
||||
#if ESP_NN
|
||||
if (output->type == kTfLiteInt8 && input->type == kTfLiteInt8) {
|
||||
const int32_t input_width = input->dims->data[1];
|
||||
const int32_t input_height = input->dims->data[2];
|
||||
int scratch_buf_size = esp_nn_get_softmax_scratch_size(input_width,
|
||||
input_height);
|
||||
if (scratch_buf_size > 0) {
|
||||
TF_LITE_ENSURE_STATUS(context->RequestScratchBufferInArena(
|
||||
context, scratch_buf_size, &data->buffer_idx));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SOFTMAX() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,79 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/exp.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, output->type, input->type);
|
||||
TF_LITE_ENSURE_EQ(context, output->bytes, input->bytes);
|
||||
TF_LITE_ENSURE_EQ(context, output->dims->size, input->dims->size);
|
||||
for (int i = 0; i < output->dims->size; ++i) {
|
||||
TF_LITE_ENSURE_EQ(context, output->dims->data[i], input->dims->data[i]);
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
int flat_size = MatchingFlatSize(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorShape(output));
|
||||
|
||||
if (input->type == kTfLiteFloat32) {
|
||||
reference_ops::Exp(tflite::micro::GetTensorData<float>(input),
|
||||
static_cast<size_t>(flat_size),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
} else {
|
||||
MicroPrintf("Type %s (%d) currently not supported by Exp.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_EXP() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,148 +0,0 @@
|
||||
/* Copyright 2021 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"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kAxisTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context,
|
||||
const TfLiteTensor* axis,
|
||||
int32_t* axis_value) {
|
||||
const int axis_dims = (tflite::GetTensorShape(axis)).DimensionsCount();
|
||||
if (axis_dims > 1) {
|
||||
MicroPrintf("Axis has only one element for Expand_Dims.", axis_dims);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (kTfLiteInt32 == (axis->type)) {
|
||||
const int32_t* axis_ptr = tflite::GetTensorData<int32_t>(axis);
|
||||
*axis_value = axis_ptr[0];
|
||||
return kTfLiteOk;
|
||||
} else {
|
||||
MicroPrintf("Axis type %s (%d) not supported by Expand_Dims.",
|
||||
TfLiteTypeGetName(axis->type), axis->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that the output tensor's dimension shape is equivalent to inserting
|
||||
// a dimension of length 1 at the dimension index axis of input's shape as
|
||||
// defined in https://www.tensorflow.org/api_docs/python/tf/expand_dims.
|
||||
TfLiteStatus VerifyTensorDim(TfLiteContext* context, const TfLiteTensor* input,
|
||||
const TfLiteTensor* axis_tensor,
|
||||
const TfLiteTensor* output) {
|
||||
int32_t axis_value = 0;
|
||||
TF_LITE_ENSURE_OK(context,
|
||||
GetAxisValueFromTensor(context, axis_tensor, &axis_value));
|
||||
|
||||
tflite::RuntimeShape input_shape = tflite::GetTensorShape(input);
|
||||
if (axis_value < 0) {
|
||||
axis_value = input_shape.DimensionsCount() + 1 + axis_value;
|
||||
}
|
||||
TF_LITE_ENSURE(context, axis_value <= input_shape.DimensionsCount());
|
||||
|
||||
// TFLM only supports fixed dimension tensor and assumes that the output shape
|
||||
// is fully specified in the model. As such, TFLM directly use the pointer to
|
||||
// the dimension array in the model buffer.
|
||||
tflite::RuntimeShape output_shape = tflite::GetTensorShape(output);
|
||||
|
||||
TF_LITE_ENSURE(context, output_shape.DimensionsCount() ==
|
||||
input_shape.DimensionsCount() + 1);
|
||||
for (int i = 0; i < output_shape.DimensionsCount(); ++i) {
|
||||
if (i < axis_value) {
|
||||
TF_LITE_ENSURE(context, output_shape.Dims(i) == input_shape.Dims(i));
|
||||
} else if (i == axis_value) {
|
||||
TF_LITE_ENSURE(context, output_shape.Dims(i) == 1);
|
||||
} else {
|
||||
TF_LITE_ENSURE(context, output_shape.Dims(i) == input_shape.Dims(i - 1));
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* axis =
|
||||
micro_context->AllocateTempInputTensor(node, kAxisTensor);
|
||||
TF_LITE_ENSURE(context, axis != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
output->type = input->type;
|
||||
if (IsDynamicTensor(axis)) {
|
||||
MicroPrintf("DynamicTensor is not yet supported by Expand_Dims.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_OK(context, VerifyTensorDim(context, input, axis, output));
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(axis);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void memCopyN(T* out, const T* in, const int num_elements) {
|
||||
for (int i = 0; i < num_elements; ++i) {
|
||||
out[i] = in[i];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
const int flat_size = ElementCount(*input->dims);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
memCopyN(tflite::micro::GetTensorData<float>(output),
|
||||
tflite::micro::GetTensorData<float>(input), flat_size);
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
memCopyN(tflite::micro::GetTensorData<int8_t>(output),
|
||||
tflite::micro::GetTensorData<int8_t>(input), flat_size);
|
||||
} break;
|
||||
default:
|
||||
MicroPrintf(
|
||||
"Expand_Dims only currently supports int8 and float32, got %d.",
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_EXPAND_DIMS() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,139 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/fill.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus EnsureEqImpl(TfLiteContext* context, const TfLiteIntArray* array,
|
||||
const TfLiteTensor* tensor) {
|
||||
for (int i = 0; i < array->size; ++i) {
|
||||
TF_LITE_ENSURE_EQ(context, array->data[i], GetTensorData<T>(tensor)[i]);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Ensure the equality of an int array and a tensor, which must be
|
||||
// one-dimensional and of an integer type.
|
||||
TfLiteStatus EnsureEq(TfLiteContext* context, const TfLiteIntArray* array,
|
||||
const TfLiteTensor* tensor) {
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(tensor), 1);
|
||||
const auto tensor_len = tensor->dims->data[0];
|
||||
TF_LITE_ENSURE_EQ(context, array->size, tensor_len);
|
||||
|
||||
switch (tensor->type) {
|
||||
case kTfLiteInt8:
|
||||
return EnsureEqImpl<int8_t>(context, array, tensor);
|
||||
case kTfLiteInt16:
|
||||
return EnsureEqImpl<int16_t>(context, array, tensor);
|
||||
case kTfLiteInt32:
|
||||
return EnsureEqImpl<int32_t>(context, array, tensor);
|
||||
case kTfLiteInt64:
|
||||
return EnsureEqImpl<int64_t>(context, array, tensor);
|
||||
default:
|
||||
MicroPrintf("cannot compare int array to tensor of type %d.",
|
||||
tensor->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int kDimsTensor = 0;
|
||||
constexpr int kValueTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
// Ensure inputs and outputs exist.
|
||||
TfLiteTensor* dims =
|
||||
micro_context->AllocateTempInputTensor(node, kDimsTensor);
|
||||
TF_LITE_ENSURE(context, dims != nullptr);
|
||||
TfLiteTensor* value =
|
||||
micro_context->AllocateTempInputTensor(node, kValueTensor);
|
||||
TF_LITE_ENSURE(context, value != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
// The value tensor must be a scalar.
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(value), 0);
|
||||
|
||||
// The value type and output type must match.
|
||||
TF_LITE_ENSURE_EQ(context, value->type, output->type);
|
||||
|
||||
// The dimension of the output tensor is known in model already.
|
||||
TFLITE_DCHECK(output->dims != nullptr);
|
||||
|
||||
if (dims->data.data != nullptr) {
|
||||
// When the dims tensor is specified in model already (i.e. is not an
|
||||
// activation tensor), the dims tensor must match the output tensor shape.
|
||||
// As a byproduct, ensures the dims tensor is of an integer type.
|
||||
TF_LITE_ENSURE_OK(context, EnsureEq(context, output->dims, dims));
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(dims);
|
||||
micro_context->DeallocateTempTfLiteTensor(value);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void FillImpl(const TfLiteEvalTensor* value, TfLiteEvalTensor* output) {
|
||||
reference_ops::Fill(
|
||||
micro::GetTensorShape(value), micro::GetTensorData<T>(value),
|
||||
micro::GetTensorShape(output), micro::GetTensorData<T>(output));
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* value =
|
||||
micro::GetEvalInput(context, node, kValueTensor);
|
||||
TfLiteEvalTensor* output = micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (value->type) {
|
||||
case kTfLiteFloat32:
|
||||
FillImpl<float>(value, output);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
FillImpl<int32_t>(value, output);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
FillImpl<int8_t>(value, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Fill only currently supports float32 for input 1, got %d.",
|
||||
TfLiteTypeGetName(value->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_FILL() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,129 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/floor_div.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/binary_function.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// Input/output tensor index.
|
||||
constexpr int kInputTensor1 = 0;
|
||||
constexpr int kInputTensor2 = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input1 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor1);
|
||||
TF_LITE_ENSURE(context, input1 != nullptr);
|
||||
TfLiteTensor* input2 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor2);
|
||||
TF_LITE_ENSURE(context, input2 != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, input2->type);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, output->type);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input1);
|
||||
micro_context->DeallocateTempTfLiteTensor(input2);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus EvalFloorDiv(TfLiteContext* context,
|
||||
const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output) {
|
||||
const T* denominator_data = tflite::micro::GetTensorData<T>(input2);
|
||||
|
||||
// Validate the denominator.
|
||||
for (int i = 0; i < tflite::ElementCount(*input2->dims); ++i) {
|
||||
if (std::equal_to<T>()(denominator_data[i], 0)) {
|
||||
MicroPrintf("Division by 0");
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
|
||||
if (requires_broadcast) {
|
||||
reference_ops::BroadcastBinaryFunction4DSlow<T, T, T>(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorData<T>(input1),
|
||||
tflite::micro::GetTensorShape(input2), denominator_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<T>(output), reference_ops::FloorDiv<T>);
|
||||
} else {
|
||||
reference_ops::BinaryFunction<T, T, T>(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorData<T>(input1),
|
||||
tflite::micro::GetTensorShape(input2), denominator_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<T>(output), reference_ops::FloorDiv<T>);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32: {
|
||||
return EvalFloorDiv<float>(context, input1, input2, output);
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Type '%s' is not supported by FLOOR_DIV.",
|
||||
TfLiteTypeGetName(input1->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_FLOOR_DIV() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,127 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/floor_mod.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/binary_function.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
// OLD-TODO(b/117523611): We should factor out a binary_op and put binary ops
|
||||
// there.
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// Input/output tensor index.
|
||||
constexpr int kInputTensor1 = 0;
|
||||
constexpr int kInputTensor2 = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// OLD-TODO(b/117912880): Support quantization.
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input1 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor1);
|
||||
TF_LITE_ENSURE(context, input1 != nullptr);
|
||||
TfLiteTensor* input2 =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor2);
|
||||
TF_LITE_ENSURE(context, input2 != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, input2->type);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input1->type, output->type);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input1);
|
||||
micro_context->DeallocateTempTfLiteTensor(input2);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus EvalFloorMod(TfLiteContext* context, bool requires_broadcast,
|
||||
const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output) {
|
||||
const T* denominator_data = tflite::micro::GetTensorData<T>(input2);
|
||||
|
||||
if (requires_broadcast) {
|
||||
reference_ops::BroadcastBinaryFunction4DSlow<T, T, T>(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorData<T>(input1),
|
||||
tflite::micro::GetTensorShape(input2), denominator_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<T>(output), reference_ops::FloorMod<T>);
|
||||
} else {
|
||||
reference_ops::BinaryFunction<T, T, T>(
|
||||
tflite::micro::GetTensorShape(input1),
|
||||
tflite::micro::GetTensorData<T>(input1),
|
||||
tflite::micro::GetTensorShape(input2), denominator_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<T>(output), reference_ops::FloorMod<T>);
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input1 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
const TfLiteEvalTensor* input2 =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
bool requires_broadcast = !tflite::micro::HaveSameShapes(input1, input2);
|
||||
|
||||
switch (input1->type) {
|
||||
case kTfLiteFloat32: {
|
||||
return EvalFloorMod<float>(context, requires_broadcast, input1, input2,
|
||||
output);
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Type '%s' is not supported by FLOOR_MOD.",
|
||||
TfLiteTypeGetName(input1->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_FLOOR_MOD() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,158 +0,0 @@
|
||||
/* Copyright 2022 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/micro/kernels/fully_connected.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/fully_connected.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/fully_connected.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context,
|
||||
sizeof(OpDataFullyConnected));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
auto* data = static_cast<OpDataFullyConnected*>(node->user_data);
|
||||
const auto params =
|
||||
static_cast<const TfLiteFullyConnectedParams*>(node->builtin_data);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kFullyConnectedInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* filter = micro_context->AllocateTempInputTensor(
|
||||
node, kFullyConnectedWeightsTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
TfLiteTensor* bias =
|
||||
micro_context->AllocateTempInputTensor(node, kFullyConnectedBiasTensor);
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(
|
||||
node, kFullyConnectedOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
TF_LITE_ENSURE_OK(context, CalculateOpDataFullyConnected(
|
||||
context, params->activation, input->type,
|
||||
input, filter, bias, output, data));
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(filter);
|
||||
if (bias != nullptr) {
|
||||
micro_context->DeallocateTempTfLiteTensor(bias);
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
const auto* params =
|
||||
static_cast<const TfLiteFullyConnectedParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kFullyConnectedInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
tflite::micro::GetEvalInput(context, node, kFullyConnectedWeightsTensor);
|
||||
const TfLiteEvalTensor* bias =
|
||||
tflite::micro::GetEvalInput(context, node, kFullyConnectedBiasTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kFullyConnectedOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const auto& data =
|
||||
*(static_cast<const OpDataFullyConnected*>(node->user_data));
|
||||
|
||||
// Checks in Prepare ensure input, output and filter types are all the same.
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
const float* bias_data =
|
||||
nullptr != bias ? tflite::micro::GetTensorData<float>(bias) : nullptr;
|
||||
|
||||
tflite::reference_ops::FullyConnected(
|
||||
FullyConnectedParamsFloat(params->activation),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias), bias_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
}
|
||||
|
||||
case kTfLiteInt8: {
|
||||
const int32_t* bias_data =
|
||||
nullptr != bias ? tflite::micro::GetTensorData<int32_t>(bias)
|
||||
: nullptr;
|
||||
|
||||
tflite::reference_integer_ops::FullyConnected(
|
||||
FullyConnectedParamsQuantized(data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias), bias_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
}
|
||||
|
||||
case kTfLiteInt16: {
|
||||
const int64_t* bias_data =
|
||||
nullptr != bias ? tflite::micro::GetTensorData<int64_t>(bias)
|
||||
: nullptr;
|
||||
|
||||
tflite::reference_integer_ops::FullyConnected(
|
||||
FullyConnectedParamsQuantized(data),
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias), bias_data,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_FULLY_CONNECTED() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,223 +0,0 @@
|
||||
/* Copyright 2021 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kInputPositions = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
template <typename InputT, typename CoordsT = int32_t>
|
||||
TfLiteStatus Gather(const TfLiteGatherParams* params,
|
||||
const TfLiteEvalTensor* input,
|
||||
const TfLiteEvalTensor* coords, TfLiteEvalTensor* output) {
|
||||
const InputT* input_data = tflite::micro::GetTensorData<InputT>(input);
|
||||
const CoordsT* coords_data = tflite::micro::GetTensorData<CoordsT>(coords);
|
||||
InputT* output_data = tflite::micro::GetTensorData<InputT>(output);
|
||||
const TfLiteIntArray* input_dims = input->dims;
|
||||
const int input_dims_size = input_dims->size;
|
||||
int axis = params->axis;
|
||||
if (axis < 0) {
|
||||
axis += input_dims_size;
|
||||
}
|
||||
TFLITE_DCHECK_GE(axis, 0);
|
||||
TFLITE_DCHECK_LT(axis, input_dims_size);
|
||||
|
||||
int batch_dims = params->batch_dims;
|
||||
// batch_dims should be in range: [-rank(coords), rank(coords)].
|
||||
// Negative batch_dims is added with rank of coords.
|
||||
const TfLiteIntArray* coords_dims = coords->dims;
|
||||
const int coords_dims_size = coords_dims->size;
|
||||
if (batch_dims < 0) {
|
||||
batch_dims += coords_dims_size;
|
||||
}
|
||||
TFLITE_DCHECK_GE(batch_dims, 0);
|
||||
TFLITE_DCHECK_LT(batch_dims, input_dims_size);
|
||||
TFLITE_DCHECK_LE(batch_dims, coords_dims_size);
|
||||
TFLITE_DCHECK_GE(axis, batch_dims);
|
||||
for (int i = 0; i < batch_dims; ++i) {
|
||||
TFLITE_DCHECK_EQ(input_dims->data[i], coords_dims->data[i]);
|
||||
}
|
||||
|
||||
const int axis_size = input_dims->data[axis];
|
||||
|
||||
int batch_size = 1;
|
||||
for (int i = 0; i < batch_dims; ++i) {
|
||||
batch_size *= input_dims->data[i];
|
||||
}
|
||||
int outer_size = 1;
|
||||
for (int i = batch_dims; i < axis; ++i) {
|
||||
outer_size *= input_dims->data[i];
|
||||
}
|
||||
int inner_size = 1;
|
||||
for (int i = axis + 1; i < input_dims_size; ++i) {
|
||||
inner_size *= input_dims->data[i];
|
||||
}
|
||||
int coord_size = 1;
|
||||
for (int i = batch_dims; i < coords_dims_size; ++i) {
|
||||
coord_size *= coords_dims->data[i];
|
||||
}
|
||||
|
||||
for (int batch = 0; batch < batch_size; ++batch) {
|
||||
for (int outer = 0; outer < outer_size; ++outer) {
|
||||
for (int coord = 0; coord < coord_size; ++coord) {
|
||||
TFLITE_DCHECK_GE(coords_data[coord], 0);
|
||||
TFLITE_DCHECK_LT(coords_data[coord], axis_size);
|
||||
std::memcpy(output_data +
|
||||
(((batch * outer_size) + outer) * coord_size + coord) *
|
||||
inner_size,
|
||||
input_data + (((batch * outer_size) + outer) * axis_size +
|
||||
coords_data[batch * coord_size + coord]) *
|
||||
inner_size,
|
||||
sizeof(InputT) * inner_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
const auto* params =
|
||||
reinterpret_cast<const TfLiteGatherParams*>(node->builtin_data);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* coords =
|
||||
micro_context->AllocateTempInputTensor(node, kInputPositions);
|
||||
TF_LITE_ENSURE(context, coords != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
switch (coords->type) {
|
||||
case kTfLiteInt32:
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Positions of type '%s' are not supported by gather.",
|
||||
TfLiteTypeGetName(coords->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
|
||||
// Assign to output the input type.
|
||||
output->type = input->type;
|
||||
|
||||
// Check conditions for different types.
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
case kTfLiteInt8:
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type '%s' is not supported by gather.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
|
||||
int axis = params->axis;
|
||||
if (axis < 0) {
|
||||
axis += NumDimensions(input);
|
||||
}
|
||||
TF_LITE_ENSURE(context, 0 <= axis && axis < NumDimensions(input));
|
||||
|
||||
int batch_dims = params->batch_dims;
|
||||
// batch_dims should be in range: [-rank(coords), rank(coords)].
|
||||
// Negative batch_dims is added with rank of coords.
|
||||
if (batch_dims < 0) {
|
||||
batch_dims += NumDimensions(coords);
|
||||
}
|
||||
TF_LITE_ENSURE(context, batch_dims <= axis);
|
||||
TF_LITE_ENSURE(context, 0 <= batch_dims && batch_dims < NumDimensions(input));
|
||||
TF_LITE_ENSURE(context, batch_dims <= NumDimensions(coords));
|
||||
for (int i = 0; i < batch_dims; ++i) {
|
||||
TF_LITE_ENSURE_EQ(context, input->dims->data[i], coords->dims->data[i]);
|
||||
}
|
||||
|
||||
// GATHER updates the output tensor dimensions, but TfLiteTensor in the
|
||||
// MicroInterpreter is a temporary allocation. We must therefore relocate the
|
||||
// dims from the FlatBuffer to the persistant storage arena.
|
||||
TfLiteEvalTensor* output_eval =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
TF_LITE_ENSURE_OK(context, tflite::micro::CreateWritableTensorDimsWithCopy(
|
||||
context, output, output_eval));
|
||||
|
||||
TfLiteIntArray* output_shape = output->dims;
|
||||
output_shape->size =
|
||||
NumDimensions(input) + NumDimensions(coords) - 1 - batch_dims;
|
||||
int output_index = 0;
|
||||
for (int i = 0; i < axis; ++i) {
|
||||
output_shape->data[output_index++] = input->dims->data[i];
|
||||
}
|
||||
for (int i = batch_dims; i < coords->dims->size; ++i) {
|
||||
output_shape->data[output_index++] = coords->dims->data[i];
|
||||
}
|
||||
for (int i = axis + 1; i < input->dims->size; ++i) {
|
||||
output_shape->data[output_index++] = input->dims->data[i];
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(coords);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const auto* params =
|
||||
reinterpret_cast<const TfLiteGatherParams*>(node->builtin_data);
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* coords =
|
||||
tflite::micro::GetEvalInput(context, node, kInputPositions);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
if (coords->type == kTfLiteInt32) {
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
return Gather<float, int32_t>(params, input, coords, output);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
return Gather<int8_t, int32_t>(params, input, coords, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type '%s' is not supported by gather.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_GATHER() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,204 +0,0 @@
|
||||
/* Copyright 2021 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"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kParams = 0;
|
||||
constexpr int kIndices = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
constexpr int MAX_INDICES_ND = 5;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* params = micro_context->AllocateTempInputTensor(node, kParams);
|
||||
TF_LITE_ENSURE(context, params != nullptr);
|
||||
TfLiteTensor* indices =
|
||||
micro_context->AllocateTempInputTensor(node, kIndices);
|
||||
TF_LITE_ENSURE(context, indices != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
switch (params->type) {
|
||||
case kTfLiteFloat32:
|
||||
case kTfLiteInt8:
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Params of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(params->type));
|
||||
return kTfLiteError;
|
||||
break;
|
||||
}
|
||||
switch (indices->type) {
|
||||
case kTfLiteInt32:
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(indices->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
const int params_rank = NumDimensions(params);
|
||||
const int indices_rank = NumDimensions(indices);
|
||||
const int indices_nd = SizeOfDimension(indices, indices_rank - 1);
|
||||
if (params_rank < 1) {
|
||||
MicroPrintf("Params must be at least a vector.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_rank < 1) {
|
||||
MicroPrintf("Indices must be at least a vector.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_nd > params_rank) {
|
||||
MicroPrintf("Index innermost dimension length must be <= params rank.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (indices_nd > MAX_INDICES_ND) {
|
||||
MicroPrintf("Index innermost dimension length must not exceed %d.",
|
||||
MAX_INDICES_ND);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Assign to output the input type.
|
||||
output->type = params->type;
|
||||
|
||||
// TFLM gather_nd does not create the output tensor, but it needs to ensure
|
||||
// that the output shape is correct. The result shape is
|
||||
// indices.shape[:-1] + params.shape[indices.shape[-1]:]
|
||||
TfLiteIntArray* output_shape = output->dims;
|
||||
int output_index = 0;
|
||||
for (int i = 0; i < indices_rank - 1; ++i) {
|
||||
output_shape->data[output_index++] = indices->dims->data[i];
|
||||
}
|
||||
for (int i = indices_nd; i < params_rank; ++i) {
|
||||
output_shape->data[output_index++] = params->dims->data[i];
|
||||
}
|
||||
output_shape->size = output_index;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(params);
|
||||
micro_context->DeallocateTempTfLiteTensor(indices);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename ParamsT, typename IndicesT>
|
||||
TfLiteStatus GatherNd(const TfLiteEvalTensor* params,
|
||||
const TfLiteEvalTensor* indices,
|
||||
TfLiteEvalTensor* output) {
|
||||
const int indices_dims = indices->dims->size;
|
||||
const int indices_nd = indices->dims->data[indices_dims - 1];
|
||||
const int params_dims = params->dims->size;
|
||||
const IndicesT* index_data = tflite::micro::GetTensorData<IndicesT>(indices);
|
||||
const ParamsT* param_data = tflite::micro::GetTensorData<ParamsT>(params);
|
||||
ParamsT* output_data = tflite::micro::GetTensorData<ParamsT>(output);
|
||||
|
||||
int n_slices = 1;
|
||||
for (int i = 0; i < indices_dims - 1; ++i) {
|
||||
n_slices *= indices->dims->data[i];
|
||||
}
|
||||
|
||||
// If indices[-1] == params.rank, fetch single elements.
|
||||
// If indices[-1] < params.rank, fetch slices.
|
||||
int slice_size = 1;
|
||||
for (int i = indices_nd; i < params_dims; ++i) {
|
||||
slice_size *= params->dims->data[i];
|
||||
}
|
||||
|
||||
int params_flat_size = ElementCount(*params->dims);
|
||||
int remain_flat_size = params_flat_size;
|
||||
|
||||
// Number of elements per dimension
|
||||
int dims_to_count[MAX_INDICES_ND];
|
||||
for (int i = 0; i < indices_nd; ++i) {
|
||||
dims_to_count[i] = remain_flat_size / params->dims->data[i];
|
||||
remain_flat_size = dims_to_count[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_slices; ++i) {
|
||||
int from_pos = 0;
|
||||
for (int j = 0; j < indices_nd; ++j) {
|
||||
int offset = i * indices_nd + j;
|
||||
IndicesT index = index_data[offset];
|
||||
from_pos += index * dims_to_count[j];
|
||||
}
|
||||
if (from_pos < 0 || from_pos + slice_size > params_flat_size) {
|
||||
return kTfLiteError;
|
||||
}
|
||||
std::memcpy(output_data + i * slice_size, param_data + from_pos,
|
||||
sizeof(ParamsT) * slice_size);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename IndicesT>
|
||||
TfLiteStatus EvalGatherNd(TfLiteContext* context,
|
||||
const TfLiteEvalTensor* params,
|
||||
const TfLiteEvalTensor* indices,
|
||||
TfLiteEvalTensor* output) {
|
||||
TfLiteStatus status = kTfLiteError;
|
||||
switch (params->type) {
|
||||
case kTfLiteFloat32:
|
||||
status = GatherNd<float, IndicesT>(params, indices, output);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
status = GatherNd<int8_t, IndicesT>(params, indices, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Params type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(params->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
if (status != kTfLiteOk) {
|
||||
MicroPrintf("gather_nd index out of bounds");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* params =
|
||||
tflite::micro::GetEvalInput(context, node, kParams);
|
||||
const TfLiteEvalTensor* indices =
|
||||
tflite::micro::GetEvalInput(context, node, kIndices);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (indices->type) {
|
||||
case kTfLiteInt32:
|
||||
return EvalGatherNd<int32_t>(context, params, indices, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
|
||||
TfLiteTypeGetName(indices->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_GATHER_ND() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,122 +0,0 @@
|
||||
/* Copyright 2020 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/micro/kernels/kernel_runner.h"
|
||||
|
||||
#include "tensorflow/lite/micro/arena_allocator/single_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_arena_constants.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/test_helpers.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
|
||||
// TODO(b/161841696): Consider moving away from global arena buffers:
|
||||
constexpr int KernelRunner::kKernelRunnerBufferSize_;
|
||||
uint8_t KernelRunner::kKernelRunnerBuffer_[];
|
||||
|
||||
void ClearBufferApi(TfLiteContext* context_) {
|
||||
context_->GetScratchBuffer = nullptr;
|
||||
context_->GetExternalContext = nullptr;
|
||||
context_->AllocatePersistentBuffer = nullptr;
|
||||
context_->RequestScratchBufferInArena = nullptr;
|
||||
}
|
||||
|
||||
KernelRunner::KernelRunner(const TfLiteRegistration& registration,
|
||||
TfLiteTensor* tensors, int tensors_size,
|
||||
TfLiteIntArray* inputs, TfLiteIntArray* outputs,
|
||||
void* builtin_data, TfLiteIntArray* intermediates)
|
||||
: registration_(registration),
|
||||
allocator_(SingleArenaBufferAllocator::Create(GetMicroErrorReporter(),
|
||||
kKernelRunnerBuffer_,
|
||||
kKernelRunnerBufferSize_)),
|
||||
mock_micro_graph_(allocator_),
|
||||
fake_micro_context_(tensors, allocator_, &mock_micro_graph_) {
|
||||
// Prepare TfLiteContext:
|
||||
context_.impl_ = static_cast<void*>(&fake_micro_context_);
|
||||
context_.ReportError = MicroContextReportOpError;
|
||||
context_.recommended_num_threads = 1;
|
||||
context_.GetTensor = MicroContextGetTensor;
|
||||
context_.GetEvalTensor = MicroContextGetEvalTensor;
|
||||
tflite::micro::ClearBufferApi(&context_);
|
||||
context_.AllocatePersistentBuffer = MicroContextAllocatePersistentBuffer;
|
||||
|
||||
context_.recommended_num_threads = 0;
|
||||
|
||||
// Prepare TfLiteNode:
|
||||
node_.inputs = inputs;
|
||||
node_.outputs = outputs;
|
||||
node_.builtin_data = builtin_data;
|
||||
node_.intermediates = intermediates;
|
||||
}
|
||||
|
||||
bool KernelRunner::ValidateTempBufferDeallocated() {
|
||||
return fake_micro_context_.IsAllTempTfLiteTensorDeallocated();
|
||||
}
|
||||
|
||||
TfLiteStatus KernelRunner::InitAndPrepare(const char* init_data,
|
||||
size_t length) {
|
||||
if (registration_.init) {
|
||||
tflite::micro::ClearBufferApi(&context_);
|
||||
context_.AllocatePersistentBuffer = MicroContextAllocatePersistentBuffer;
|
||||
node_.user_data = registration_.init(&context_, init_data, length);
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE(&context_, ValidateTempBufferDeallocated());
|
||||
|
||||
if (registration_.prepare) {
|
||||
tflite ::micro::ClearBufferApi(&context_);
|
||||
context_.AllocatePersistentBuffer = MicroContextAllocatePersistentBuffer;
|
||||
context_.RequestScratchBufferInArena =
|
||||
MicroContextRequestScratchBufferInArena;
|
||||
context_.GetExternalContext = MicroContextGetExternalContext;
|
||||
TF_LITE_ENSURE_STATUS(registration_.prepare(&context_, &node_));
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE(&context_, ValidateTempBufferDeallocated());
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus KernelRunner::Invoke() {
|
||||
tflite::micro::ClearBufferApi(&context_);
|
||||
context_.GetScratchBuffer = MicroContextGetScratchBuffer;
|
||||
|
||||
if (registration_.invoke == nullptr) {
|
||||
MicroPrintf("TfLiteRegistration missing invoke function pointer!");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(registration_.invoke(&context_, &node_));
|
||||
|
||||
TF_LITE_ENSURE(&context_, ValidateTempBufferDeallocated());
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus KernelRunner::Free() {
|
||||
tflite::micro::ClearBufferApi(&context_);
|
||||
context_.GetScratchBuffer = MicroContextGetScratchBuffer;
|
||||
|
||||
if (registration_.free == nullptr) {
|
||||
MicroPrintf("TfLiteRegistration missing free function pointer!");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
registration_.free(&context_, node_.user_data);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
@@ -1,81 +0,0 @@
|
||||
/* Copyright 2020 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_RUNNER_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_RUNNER_H_
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/single_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/fake_micro_context.h"
|
||||
#include "tensorflow/lite/micro/mock_micro_graph.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
|
||||
// Helper class to perform a simulated kernel (i.e. TfLiteRegistration)
|
||||
// lifecycle (init, prepare, invoke). All internal allocations are handled by
|
||||
// this class. Simply pass in the registration, list of required tensors, inputs
|
||||
// array, outputs array, and any pre-builtin data. Calling Invoke() will
|
||||
// automatically walk the kernel and outputs will be ready on the TfLiteTensor
|
||||
// output provided during construction.
|
||||
class KernelRunner {
|
||||
public:
|
||||
KernelRunner(const TfLiteRegistration& registration, TfLiteTensor* tensors,
|
||||
int tensors_size, TfLiteIntArray* inputs,
|
||||
TfLiteIntArray* outputs, void* builtin_data,
|
||||
TfLiteIntArray* intermediates = nullptr);
|
||||
|
||||
// Calls init and prepare on the kernel (i.e. TfLiteRegistration) struct. Any
|
||||
// exceptions will be DebugLog'd and returned as a status code.
|
||||
TfLiteStatus InitAndPrepare(const char* init_data = nullptr,
|
||||
size_t length = 0);
|
||||
|
||||
// Calls init, prepare, and invoke on a given TfLiteRegistration pointer.
|
||||
// After successful invoke, results will be available in the output tensor as
|
||||
// passed into the constructor of this class.
|
||||
TfLiteStatus Invoke();
|
||||
|
||||
// Calls Free on a given TfLiteRegistration pointer(if it's implemented).
|
||||
// After successful Free, kTfLiteOk status will be returned. If Free is not
|
||||
// implemented for a given kernel kTfLiteError will be returned.
|
||||
TfLiteStatus Free();
|
||||
|
||||
// Returns a pointer to the internal MockMicroGraph which KernelRunner uses
|
||||
// to stub out MicroGraph methods and track invocations on each subgraph.
|
||||
MockMicroGraph* GetMockGraph() { return &mock_micro_graph_; }
|
||||
|
||||
// Returns true if all temp buffer in tests are deallocated.
|
||||
// TODO(b/209453859): move this function to private after deallocation checks
|
||||
// are enabled for all kernel tests.
|
||||
bool ValidateTempBufferDeallocated();
|
||||
|
||||
private:
|
||||
static constexpr int kKernelRunnerBufferSize_ = 10000;
|
||||
static uint8_t kKernelRunnerBuffer_[kKernelRunnerBufferSize_];
|
||||
|
||||
TfLiteContext context_ = {};
|
||||
TfLiteNode node_ = {};
|
||||
const TfLiteRegistration& registration_;
|
||||
|
||||
SingleArenaBufferAllocator* allocator_;
|
||||
MockMicroGraph mock_micro_graph_;
|
||||
FakeMicroContext fake_micro_context_;
|
||||
};
|
||||
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_RUNNER_H_
|
||||
@@ -1,260 +0,0 @@
|
||||
/* Copyright 2020 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/micro/kernels/kernel_util.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
|
||||
namespace {
|
||||
|
||||
int ValidateTensorIndexing(const TfLiteContext* context, int index,
|
||||
int max_size, const int* tensor_indices) {
|
||||
if (index >= 0 && index < max_size) {
|
||||
const int tensor_index = tensor_indices[index];
|
||||
if (tensor_index != kTfLiteOptionalTensor) {
|
||||
return tensor_index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration RegisterOp(
|
||||
void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
|
||||
void (*free)(TfLiteContext* context, void* buffer)) {
|
||||
return {/*init=*/init,
|
||||
/*free=*/free,
|
||||
/*prepare=*/prepare,
|
||||
/*invoke=*/invoke,
|
||||
/*profiling_string=*/nullptr,
|
||||
/*builtin_code=*/0,
|
||||
/*custom_name=*/nullptr,
|
||||
/*version=*/0,
|
||||
/*registration_external=*/nullptr};
|
||||
}
|
||||
|
||||
// Returns a mutable tensor for a given input index. is_variable must be checked
|
||||
// during prepare when the full TfLiteTensor is available.
|
||||
TfLiteEvalTensor* GetMutableEvalInput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index) {
|
||||
TFLITE_DCHECK(context != nullptr);
|
||||
TFLITE_DCHECK(node != nullptr);
|
||||
const int tensor_index = ValidateTensorIndexing(
|
||||
context, index, node->inputs->size, node->inputs->data);
|
||||
|
||||
if (tensor_index < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return context->GetEvalTensor(context, node->inputs->data[index]);
|
||||
}
|
||||
|
||||
// Returns the TfLiteEvalTensor struct for a given input index in a node.
|
||||
const TfLiteEvalTensor* GetEvalInput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index) {
|
||||
return GetMutableEvalInput(context, node, index);
|
||||
}
|
||||
|
||||
// Returns the TfLiteEvalTensor struct for a given output index in a node.
|
||||
TfLiteEvalTensor* GetEvalOutput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index) {
|
||||
TFLITE_DCHECK(context != nullptr);
|
||||
TFLITE_DCHECK(node != nullptr);
|
||||
return context->GetEvalTensor(context, node->outputs->data[index]);
|
||||
}
|
||||
|
||||
bool HaveSameShapes(const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2) {
|
||||
TFLITE_DCHECK(input1 != nullptr);
|
||||
TFLITE_DCHECK(input2 != nullptr);
|
||||
return TfLiteIntArrayEqual(input1->dims, input2->dims);
|
||||
}
|
||||
|
||||
const RuntimeShape GetTensorShape(const TfLiteEvalTensor* tensor) {
|
||||
if (tensor == nullptr || tensor->dims == nullptr) {
|
||||
return RuntimeShape();
|
||||
}
|
||||
TfLiteIntArray* dims = tensor->dims;
|
||||
const int dims_size = dims->size;
|
||||
const int32_t* dims_data = reinterpret_cast<const int32_t*>(dims->data);
|
||||
return RuntimeShape(dims_size, dims_data);
|
||||
}
|
||||
|
||||
PaddingType RuntimePaddingType(TfLitePadding padding) {
|
||||
switch (padding) {
|
||||
case TfLitePadding::kTfLitePaddingSame:
|
||||
return PaddingType::kSame;
|
||||
case TfLitePadding::kTfLitePaddingValid:
|
||||
return PaddingType::kValid;
|
||||
case TfLitePadding::kTfLitePaddingUnknown:
|
||||
default:
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
// Relocate tensor dims from FlatBuffer to the persistent storage arena.
|
||||
// The old dims data is copied to the new storage area.
|
||||
// The tensor and eval_tensor must be the same tensor.
|
||||
// Only use during Prepare phase.
|
||||
TfLiteStatus CreateWritableTensorDimsWithCopy(TfLiteContext* context,
|
||||
TfLiteTensor* tensor,
|
||||
TfLiteEvalTensor* eval_tensor) {
|
||||
TF_LITE_ENSURE(context, tensor != nullptr);
|
||||
TF_LITE_ENSURE(context, eval_tensor != nullptr);
|
||||
TF_LITE_ENSURE(context, context->AllocatePersistentBuffer != nullptr);
|
||||
int ranks = tensor->dims->size;
|
||||
size_t alloc_size = TfLiteIntArrayGetSizeInBytes(ranks);
|
||||
TfLiteIntArray* new_dims = static_cast<TfLiteIntArray*>(
|
||||
context->AllocatePersistentBuffer(context, alloc_size));
|
||||
TfLiteIntArray* old_dims = tensor->dims;
|
||||
new_dims->size = ranks;
|
||||
tensor->dims = new_dims;
|
||||
eval_tensor->dims = new_dims;
|
||||
for (int i = 0; i < ranks; i++) {
|
||||
new_dims->data[i] = old_dims->data[i];
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Verify that both tensors have the same type and size, then return the size
|
||||
// of both tensors in bytes if they are the same, or -1 if they are different.
|
||||
size_t ValidateAndGetTensorSizes(const TfLiteEvalTensor* tensor1,
|
||||
const TfLiteEvalTensor* tensor2) {
|
||||
TFLITE_DCHECK(tensor1->type == tensor2->type);
|
||||
size_t tensor1_size = 0;
|
||||
size_t tensor2_size = 0;
|
||||
TfLiteEvalTensorByteLength(tensor1, &tensor1_size);
|
||||
TfLiteEvalTensorByteLength(tensor2, &tensor2_size);
|
||||
return (tensor1_size == tensor2_size) ? tensor1_size : -1;
|
||||
}
|
||||
|
||||
TfLiteStatus CopyOpInputsToOpOutputs(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE(context, node->inputs->size == node->outputs->size);
|
||||
for (int i = 0; i < node->inputs->size; i++) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, i);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, i);
|
||||
int bytes = ValidateAndGetTensorSizes(input, output);
|
||||
TF_LITE_ENSURE(context, bytes >= 0);
|
||||
memcpy(output->data.raw, input->data.raw, bytes);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Args:
|
||||
// 1. int8_t tensor_data - int8_t buffer of unknown size who's data you'd
|
||||
// like
|
||||
// to print
|
||||
// 2. int n_btyes - a small int representing number of bytes you want to
|
||||
// print
|
||||
// to debug output. It should always be <= tensor_data's size.
|
||||
// 3. prefix - optional message you'd like to print before printing bytes
|
||||
//
|
||||
// Purpose:
|
||||
// Function takes in paramaters above and prints n_bytes bytes from the
|
||||
// tensor_data buffer. This can be use to debug the output of a model and it's
|
||||
// op.
|
||||
|
||||
void PrintNBytes(const int8_t* tensor_data, int n_bytes, const char* prefix) {
|
||||
if (prefix != nullptr) {
|
||||
MicroPrintf("%s", prefix);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_bytes; ++i) {
|
||||
MicroPrintf(" %x", tensor_data[i]);
|
||||
}
|
||||
MicroPrintf("\n");
|
||||
}
|
||||
|
||||
// same as the PrintNBytes above but the buffer needs to be extracted out of the
|
||||
// TfLiteEvalTensor*
|
||||
void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
|
||||
const char* prefix) {
|
||||
const int8_t* tensor_data = tflite::micro::GetTensorData<int8_t>(tensor);
|
||||
PrintNBytes(tensor_data, n_bytes, prefix);
|
||||
}
|
||||
|
||||
// same as the PrintNBytes above but the buffer needs to be extracted out of the
|
||||
// TfLiteEvalTensor*
|
||||
void PrintNBytes(const TfLiteTensor* tensor, int n_bytes, const char* prefix) {
|
||||
const int8_t* tensor_data = tflite::GetTensorData<int8_t>(tensor);
|
||||
PrintNBytes(tensor_data, n_bytes, prefix);
|
||||
}
|
||||
|
||||
TfLiteStatus CopyOpInputsToSubgraphInputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx,
|
||||
int first_tensor_idx) {
|
||||
TF_LITE_ENSURE(context,
|
||||
static_cast<size_t>(node->inputs->size - first_tensor_idx) ==
|
||||
graph_info->NumSubgraphInputs(subgraph_idx));
|
||||
for (int i = 0; i < node->inputs->size - first_tensor_idx; i++) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, i + first_tensor_idx);
|
||||
TfLiteEvalTensor* subgraph_input =
|
||||
graph_info->GetSubgraphInput(subgraph_idx, i);
|
||||
int bytes = ValidateAndGetTensorSizes(input, subgraph_input);
|
||||
TF_LITE_ENSURE(context, bytes >= 0);
|
||||
memcpy(subgraph_input->data.raw, input->data.raw, bytes);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus CopyOpOutputsToSubgraphInputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx) {
|
||||
TF_LITE_ENSURE(context, static_cast<size_t>(node->outputs->size) ==
|
||||
graph_info->NumSubgraphInputs(subgraph_idx));
|
||||
for (int i = 0; i < node->outputs->size; i++) {
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, i);
|
||||
TfLiteEvalTensor* subgraph_input =
|
||||
graph_info->GetSubgraphInput(subgraph_idx, i);
|
||||
int bytes = ValidateAndGetTensorSizes(output, subgraph_input);
|
||||
TF_LITE_ENSURE(context, bytes >= 0);
|
||||
memcpy(subgraph_input->data.raw, output->data.raw, bytes);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus CopySubgraphOutputsToOpOutputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx) {
|
||||
TF_LITE_ENSURE(context, static_cast<size_t>(node->outputs->size) ==
|
||||
graph_info->NumSubgraphOutputs(subgraph_idx));
|
||||
for (int i = 0; i < node->outputs->size; i++) {
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, i);
|
||||
TfLiteEvalTensor* subgraph_output =
|
||||
graph_info->GetSubgraphOutput(subgraph_idx, i);
|
||||
int bytes = ValidateAndGetTensorSizes(output, subgraph_output);
|
||||
TF_LITE_ENSURE(context, bytes >= 0);
|
||||
memcpy(output->data.raw, subgraph_output->data.raw, bytes);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
@@ -1,138 +0,0 @@
|
||||
/* Copyright 2021 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_UTIL_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_UTIL_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/micro/micro_context.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro {
|
||||
|
||||
TfLiteRegistration RegisterOp(
|
||||
void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
|
||||
TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
|
||||
TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
|
||||
void (*free)(TfLiteContext* context, void* buffer) = nullptr);
|
||||
|
||||
// Prints out n bytes in a int8_t buffer as hex
|
||||
void PrintNBytes(const int8_t* tensor_data, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Prints out the the n bytes in a TfLiteEvalTensor as hex
|
||||
void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Prints out the the n bytes in a TfLiteTensor as hex
|
||||
void PrintNBytes(const TfLiteTensor* tensor, int n_bytes,
|
||||
const char* prefix = nullptr);
|
||||
|
||||
// Returns a mutable tensor for a given input index. is_variable must be checked
|
||||
// during prepare when the full TfLiteTensor is available.
|
||||
TfLiteEvalTensor* GetMutableEvalInput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index);
|
||||
|
||||
// Returns the TfLiteEvalTensor struct for a given input index in a node.
|
||||
const TfLiteEvalTensor* GetEvalInput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index);
|
||||
|
||||
// Returns the TfLiteEvalTensor struct for a given output index in a node.
|
||||
TfLiteEvalTensor* GetEvalOutput(const TfLiteContext* context,
|
||||
const TfLiteNode* node, int index);
|
||||
|
||||
// Returns data for a TfLiteEvalTensor struct that are expected to exist.
|
||||
template <typename T>
|
||||
T* GetTensorData(TfLiteEvalTensor* tensor) {
|
||||
TFLITE_DCHECK(tensor != nullptr);
|
||||
return reinterpret_cast<T*>(tensor->data.raw);
|
||||
}
|
||||
|
||||
// Returns const data for a TfLiteEvalTensor struct that are expected to exist.
|
||||
template <typename T>
|
||||
const T* GetTensorData(const TfLiteEvalTensor* tensor) {
|
||||
TFLITE_DCHECK(tensor != nullptr);
|
||||
return reinterpret_cast<const T*>(tensor->data.raw);
|
||||
}
|
||||
|
||||
// Returns data for a TfLiteEvalTensor struct that could be null.
|
||||
template <typename T>
|
||||
T* GetOptionalTensorData(TfLiteEvalTensor* tensor) {
|
||||
return tensor == nullptr ? nullptr : reinterpret_cast<T*>(tensor->data.raw);
|
||||
}
|
||||
|
||||
// Returns const data for a TfLiteEvalTensor struct that could be null.
|
||||
template <typename T>
|
||||
const T* GetOptionalTensorData(const TfLiteEvalTensor* tensor) {
|
||||
return tensor == nullptr ? nullptr
|
||||
: reinterpret_cast<const T*>(tensor->data.raw);
|
||||
}
|
||||
|
||||
// Returns the shape of a TfLiteEvalTensor struct.
|
||||
const RuntimeShape GetTensorShape(const TfLiteEvalTensor* tensor);
|
||||
|
||||
// Return true if the given tensors have the same shape.
|
||||
bool HaveSameShapes(const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2);
|
||||
|
||||
PaddingType RuntimePaddingType(TfLitePadding padding);
|
||||
|
||||
// Relocate tensor dims from FlatBuffer to the persistent storage arena.
|
||||
// The old dims data is copied to the new storage area.
|
||||
// The tensor and eval_tensor must be the same tensor.
|
||||
// Only use during Prepare phase.
|
||||
TfLiteStatus CreateWritableTensorDimsWithCopy(TfLiteContext* context,
|
||||
TfLiteTensor* tensor,
|
||||
TfLiteEvalTensor* eval_tensor);
|
||||
|
||||
// Copy all op input tensors to op output tensors. Requires all op input tensor
|
||||
// shapes and types to be identical to op output tensor shapes and types.
|
||||
TfLiteStatus CopyOpInputsToOpOutputs(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// Copy all op input tensors to subgraph input tensors. Requires all op input
|
||||
// tensor shapes and types to be identical to subgraph input tensor shapes and
|
||||
// types.
|
||||
TfLiteStatus CopyOpInputsToSubgraphInputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx,
|
||||
int first_tensor_idx);
|
||||
|
||||
// Copy all op output tensors to subgraph input tensors. Requires all op output
|
||||
// tensor shapes and types to be identical to subgraph input tensor shapes and
|
||||
// types.
|
||||
TfLiteStatus CopyOpOutputsToSubgraphInputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx);
|
||||
|
||||
// Copy all subgraph output tensors to op outputs. Requires all subgraph output
|
||||
// tensor shapes and types to be identical to op output tensor shapes and types.
|
||||
TfLiteStatus CopySubgraphOutputsToOpOutputs(TfLiteContext* context,
|
||||
TfLiteNode* node,
|
||||
MicroGraph* graph_info,
|
||||
int subgraph_idx);
|
||||
|
||||
} // namespace micro
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_KERNEL_UTIL_H_
|
||||
@@ -1,141 +0,0 @@
|
||||
/* Copyright 2021 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/pooling.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// Input/output tensor index.
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// required rank for input/output tensor shape
|
||||
constexpr int kTensorShapeRank = 4;
|
||||
|
||||
// input/output tensor shape rank associations
|
||||
enum { kBatchRank = 0, kHeightRank, kWidthRank, kChannelRank };
|
||||
|
||||
TfLiteStatus L2Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
auto* params = static_cast<TfLitePoolParams*>(node->builtin_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(input), kTensorShapeRank);
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(output), kTensorShapeRank);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
int batches = SizeOfDimension(input, kBatchRank);
|
||||
int height = SizeOfDimension(input, kHeightRank);
|
||||
int width = SizeOfDimension(input, kWidthRank);
|
||||
int channels_out = SizeOfDimension(input, kChannelRank);
|
||||
|
||||
// Matching GetWindowedOutputSize in TensorFlow.
|
||||
auto padding = params->padding;
|
||||
int out_width, out_height;
|
||||
|
||||
params->computed.padding = ComputePaddingHeightWidth(
|
||||
params->stride_height, params->stride_width, 1, 1, height, width,
|
||||
params->filter_height, params->filter_width, padding, &out_height,
|
||||
&out_width);
|
||||
|
||||
// We currently don't have a quantized implementation of L2Pool
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
|
||||
|
||||
// We must update the output tensor dimensions.
|
||||
// The dims storage is expected to be the same area in memory
|
||||
// for both TfLiteTensor and TfLiteEvalTensor. This is important
|
||||
// because TfLiteTensor in the MicroInterpreter is a temporary
|
||||
// allocation. For the KernelRunner interpreter, TfLiteEvalTensor
|
||||
// is a temporary allocation. We must therefore relocate the dims
|
||||
// from the FlatBuffer to the persistant storage arena.
|
||||
TfLiteEvalTensor* output_eval =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
TF_LITE_ENSURE_OK(context, tflite::micro::CreateWritableTensorDimsWithCopy(
|
||||
context, output, output_eval));
|
||||
output->dims->data[kBatchRank] = batches;
|
||||
output->dims->data[kHeightRank] = out_height;
|
||||
output->dims->data[kWidthRank] = out_width;
|
||||
output->dims->data[kChannelRank] = channels_out;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void L2EvalFloat(const TfLitePoolParams& params, const TfLiteEvalTensor& input,
|
||||
tflite::PoolParams* op_params, TfLiteEvalTensor* output) {
|
||||
float activation_min, activation_max;
|
||||
CalculateActivationRange(params.activation, &activation_min, &activation_max);
|
||||
|
||||
op_params->float_activation_min = activation_min;
|
||||
op_params->float_activation_max = activation_max;
|
||||
reference_ops::L2Pool(*op_params, tflite::micro::GetTensorShape(&input),
|
||||
tflite::micro::GetTensorData<float>(&input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
}
|
||||
|
||||
TfLiteStatus L2Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params = static_cast<const TfLitePoolParams*>(node->builtin_data);
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
|
||||
tflite::PoolParams op_params;
|
||||
op_params.stride_height = params->stride_height;
|
||||
op_params.stride_width = params->stride_width;
|
||||
op_params.filter_height = params->filter_height;
|
||||
op_params.filter_width = params->filter_width;
|
||||
op_params.padding_values.height = params->computed.padding.height;
|
||||
op_params.padding_values.width = params->computed.padding.width;
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
L2EvalFloat(*params, *input, &op_params, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("L2_POOL_2D only supports float32 currently, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_L2_POOL_2D() {
|
||||
return tflite::micro::RegisterOp(nullptr, L2Prepare, L2Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,147 +0,0 @@
|
||||
/* Copyright 2017 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"
|
||||
#include "tensorflow/lite/kernels/internal/portable_tensor.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/l2normalization.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/l2normalization.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace l2norm {
|
||||
|
||||
namespace {
|
||||
|
||||
// This file has two implementation of L2Norm.
|
||||
enum KernelType {
|
||||
kReference,
|
||||
kGenericOptimized,
|
||||
};
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
auto* params = reinterpret_cast<TfLiteL2NormParams*>(node->builtin_data);
|
||||
L2NormalizationParams* data =
|
||||
static_cast<L2NormalizationParams*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) <= 4);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
output->type == kTfLiteFloat32 || output->type == kTfLiteInt8);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
if (output->type == kTfLiteInt8) {
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
} else if (output->type == kTfLiteFloat32) {
|
||||
data->input_zero_point = 0;
|
||||
}
|
||||
|
||||
// Our implementations don't currently support activations.
|
||||
TF_LITE_ENSURE_EQ(context, params->activation, kTfLiteActNone);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context,
|
||||
sizeof(L2NormalizationParams));
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const L2NormalizationParams& data =
|
||||
*(static_cast<const L2NormalizationParams*>(node->user_data));
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
// TODO(b/143912164): instead of hardcode the epsilon here, we should read it
|
||||
// from tensorflow, i.e., adding a params.
|
||||
// We don't compute epsilon for quantized kernel:
|
||||
//
|
||||
// epsilon_float = (epsilon_quant - zp) * scale
|
||||
// so
|
||||
// espsilon_quant = epsilon_float / scale + zp
|
||||
// We know epsilon_float is just a very small number to avoid division by
|
||||
// zero error, and scale is > 1, so the integer value of epsilon for quant
|
||||
// is just dominated by the zero point.
|
||||
// Also, GetInvSqrtQuantizedMultiplierExp handles the scenario where the sum
|
||||
// of input value squared is zero case well.
|
||||
// So we don't even need to do handle the epsilon for quantized kernel case.
|
||||
const float epsilon = 1e-6f;
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
reference_ops::L2Normalization(data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output),
|
||||
epsilon);
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
const auto input_shape = tflite::micro::GetTensorShape(input);
|
||||
const auto output_shape = tflite::micro::GetTensorShape(output);
|
||||
const int trailing_dim = input_shape.DimensionsCount() - 1;
|
||||
const int depth =
|
||||
MatchingDim(input_shape, trailing_dim, output_shape, trailing_dim);
|
||||
const int outer_size =
|
||||
MatchingFlatSizeSkipDim(input_shape, trailing_dim, output_shape);
|
||||
reference_integer_ops::L2Normalization(
|
||||
data.input_zero_point, outer_size, depth,
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
MicroPrintf("Output type is %s, requires float.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace l2norm
|
||||
|
||||
TfLiteRegistration Register_L2NORM_REF() {
|
||||
return tflite::micro::RegisterOp(l2norm::Init, l2norm::Prepare, l2norm::Eval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_L2_NORMALIZATION() { return Register_L2NORM_REF(); }
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,147 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/log_softmax.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// used only with quantized data
|
||||
struct LogSoftmaxOpData {
|
||||
int32_t input_multiplier;
|
||||
int32_t input_left_shift;
|
||||
int32_t reverse_scaling_divisor;
|
||||
int32_t reverse_scaling_right_shift;
|
||||
int diff_min;
|
||||
size_t outer_size; // number of tensor elements skipping computation axis
|
||||
size_t depth; // number of tensor elements on computation axis
|
||||
};
|
||||
|
||||
// input/output tensor index
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
TF_LITE_ENSURE(context, HaveSameShapes(input, output));
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
node->user_data =
|
||||
context->AllocatePersistentBuffer(context, sizeof(LogSoftmaxOpData));
|
||||
auto data = static_cast<LogSoftmaxOpData*>(node->user_data);
|
||||
|
||||
// quantization datum
|
||||
constexpr int32_t kOutputZeroPoint = 127;
|
||||
constexpr float kOutputScale = 16.0 / 256;
|
||||
constexpr double kBeta = 1.0;
|
||||
constexpr int kScaledDiffIntegerBits = 5;
|
||||
|
||||
TF_LITE_ENSURE(context, output->params.scale == kOutputScale);
|
||||
TF_LITE_ENSURE(context, output->params.zero_point == kOutputZeroPoint);
|
||||
|
||||
int input_left_shift;
|
||||
int reverse_scaling_right_shift;
|
||||
tflite::PreprocessLogSoftmaxScalingExp(
|
||||
kBeta, static_cast<double>(input->params.scale), kScaledDiffIntegerBits,
|
||||
&data->input_multiplier, &input_left_shift,
|
||||
&data->reverse_scaling_divisor, &reverse_scaling_right_shift);
|
||||
data->input_left_shift = static_cast<int32_t>(input_left_shift);
|
||||
data->reverse_scaling_right_shift =
|
||||
static_cast<int32_t>(-reverse_scaling_right_shift);
|
||||
// diff_min has a negative value, and is used to limit the maximum magnitude
|
||||
// of the diffs, which are <= 0.
|
||||
data->diff_min =
|
||||
-tflite::CalculateInputRadius(kScaledDiffIntegerBits, input_left_shift);
|
||||
|
||||
RuntimeShape input_shape = GetTensorShape(input);
|
||||
const int trailing_dim = input_shape.DimensionsCount() - 1;
|
||||
data->outer_size =
|
||||
static_cast<size_t>(FlatSizeSkipDim(input_shape, trailing_dim));
|
||||
data->depth = static_cast<size_t>(input_shape.Dims(trailing_dim));
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus LogSoftmaxPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
return CalculateOpData(context, node);
|
||||
}
|
||||
|
||||
TfLiteStatus LogSoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const LogSoftmaxOpData* data =
|
||||
static_cast<LogSoftmaxOpData*>(node->user_data);
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
SoftmaxParams op_params = {};
|
||||
reference_ops::LogSoftmax(op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
SoftmaxParams op_params = {};
|
||||
op_params.input_multiplier = data->input_multiplier;
|
||||
op_params.input_left_shift = data->input_left_shift;
|
||||
op_params.reverse_scaling_divisor = data->reverse_scaling_divisor;
|
||||
op_params.reverse_scaling_right_shift = data->reverse_scaling_right_shift;
|
||||
op_params.diff_min = data->diff_min;
|
||||
reference_ops::LogSoftmax(op_params, data->outer_size, data->depth,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("LOG_SOFTMAX only supports float32, int8, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_LOG_SOFTMAX() {
|
||||
return tflite::micro::RegisterOp(nullptr, LogSoftmaxPrepare, LogSoftmaxEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,131 +0,0 @@
|
||||
/* Copyright 2018 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/kernels/internal/reference/maximum_minimum.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace maximum_minimum {
|
||||
namespace {
|
||||
|
||||
// This file has a reference implementation of TFMaximum/TFMinimum.
|
||||
enum KernelType {
|
||||
kReference,
|
||||
};
|
||||
|
||||
constexpr int kInputTensor1 = 0;
|
||||
constexpr int kInputTensor2 = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpContext {
|
||||
OpContext(TfLiteContext* context, TfLiteNode* node) {
|
||||
input1 = tflite::micro::GetEvalInput(context, node, kInputTensor1);
|
||||
input2 = tflite::micro::GetEvalInput(context, node, kInputTensor2);
|
||||
output = tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
}
|
||||
const TfLiteEvalTensor* input1;
|
||||
const TfLiteEvalTensor* input2;
|
||||
TfLiteEvalTensor* output;
|
||||
};
|
||||
|
||||
struct MaximumOp {
|
||||
template <typename data_type>
|
||||
static data_type op(data_type el1, data_type el2) {
|
||||
return el1 > el2 ? el1 : el2;
|
||||
}
|
||||
};
|
||||
|
||||
struct MinimumOp {
|
||||
template <typename data_type>
|
||||
static data_type op(data_type el1, data_type el2) {
|
||||
return el1 < el2 ? el1 : el2;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename data_type, typename op_type>
|
||||
void TFLiteOperation(TfLiteContext* context, TfLiteNode* node,
|
||||
const OpContext& op_context) {
|
||||
reference_ops::MaximumMinimumBroadcastSlow(
|
||||
tflite::micro::GetTensorShape(op_context.input1),
|
||||
tflite::micro::GetTensorData<data_type>(op_context.input1),
|
||||
tflite::micro::GetTensorShape(op_context.input2),
|
||||
tflite::micro::GetTensorData<data_type>(op_context.input2),
|
||||
tflite::micro::GetTensorShape(op_context.output),
|
||||
tflite::micro::GetTensorData<data_type>(op_context.output),
|
||||
op_type::template op<data_type>);
|
||||
}
|
||||
|
||||
template <KernelType kernel_type, typename OpType>
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpContext op_context(context, node);
|
||||
|
||||
if (kernel_type == kReference) {
|
||||
switch (op_context.output->type) {
|
||||
case kTfLiteFloat32:
|
||||
TFLiteOperation<float, OpType>(context, node, op_context);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
TFLiteOperation<int8_t, OpType>(context, node, op_context);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
TFLiteOperation<int32_t, OpType>(context, node, op_context);
|
||||
break;
|
||||
case kTfLiteInt64:
|
||||
TFLiteOperation<int64_t, OpType>(context, node, op_context);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) is not supported by Maximum/Minimum.",
|
||||
TfLiteTypeGetName(op_context.output->type),
|
||||
op_context.output->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
} else {
|
||||
MicroPrintf("Kernel type not supported by Maximum/Minimum.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace maximum_minimum
|
||||
|
||||
TfLiteRegistration Register_MAXIMUM() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, nullptr,
|
||||
maximum_minimum::Eval<maximum_minimum::kReference,
|
||||
maximum_minimum::MaximumOp>);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_MINIMUM() {
|
||||
return tflite::micro::RegisterOp(
|
||||
nullptr, nullptr,
|
||||
maximum_minimum::Eval<maximum_minimum::kReference,
|
||||
maximum_minimum::MinimumOp>);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,135 +0,0 @@
|
||||
/* Copyright 2021 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_MICRO_OPS_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_MICRO_OPS_H_
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
// Forward declaration of all micro op kernel registration methods. These
|
||||
// registrations are included with the standard `BuiltinOpResolver`.
|
||||
//
|
||||
// This header is particularly useful in cases where only a subset of ops are
|
||||
// needed. In such cases, the client can selectively add only the registrations
|
||||
// their model requires, using a custom `(Micro)MutableOpResolver`. Selective
|
||||
// registration in turn allows the linker to strip unused kernels.
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// TFLM is incrementally moving towards a flat tflite namespace
|
||||
// (https://abseil.io/tips/130). Any new ops (or cleanup of existing ops should
|
||||
// have their Register function declarations in the tflite namespace.
|
||||
|
||||
TfLiteRegistration Register_ADD();
|
||||
TfLiteRegistration Register_ADD_N();
|
||||
TfLiteRegistration Register_ASSIGN_VARIABLE();
|
||||
TfLiteRegistration Register_AVERAGE_POOL_2D();
|
||||
TfLiteRegistration Register_BATCH_TO_SPACE_ND();
|
||||
TfLiteRegistration Register_BROADCAST_ARGS();
|
||||
TfLiteRegistration Register_BROADCAST_TO();
|
||||
TfLiteRegistration Register_CALL_ONCE();
|
||||
TfLiteRegistration Register_CAST();
|
||||
// TODO(b/160234179): Change custom OPs to also return by value.
|
||||
TfLiteRegistration* Register_CIRCULAR_BUFFER();
|
||||
TfLiteRegistration Register_CUMSUM();
|
||||
TfLiteRegistration Register_DEPTH_TO_SPACE();
|
||||
TfLiteRegistration Register_DEPTHWISE_CONV_2D();
|
||||
TfLiteRegistration Register_DEQUANTIZE();
|
||||
TfLiteRegistration Register_DIV();
|
||||
TfLiteRegistration Register_ELU();
|
||||
TfLiteRegistration Register_EXP();
|
||||
TfLiteRegistration Register_EXPAND_DIMS();
|
||||
TfLiteRegistration Register_FILL();
|
||||
TfLiteRegistration Register_FLOOR_DIV();
|
||||
TfLiteRegistration Register_FLOOR_MOD();
|
||||
TfLiteRegistration Register_GATHER();
|
||||
TfLiteRegistration Register_GATHER_ND();
|
||||
TfLiteRegistration Register_HARD_SWISH();
|
||||
TfLiteRegistration Register_IF();
|
||||
TfLiteRegistration Register_L2_POOL_2D();
|
||||
TfLiteRegistration Register_LEAKY_RELU();
|
||||
TfLiteRegistration Register_LOG_SOFTMAX();
|
||||
TfLiteRegistration Register_LOGICAL_AND();
|
||||
TfLiteRegistration Register_LOGICAL_OR();
|
||||
TfLiteRegistration Register_LOGISTIC();
|
||||
TfLiteRegistration Register_MAX_POOL_2D();
|
||||
TfLiteRegistration Register_MIRROR_PAD();
|
||||
TfLiteRegistration Register_PRELU();
|
||||
TfLiteRegistration Register_MUL();
|
||||
TfLiteRegistration Register_QUANTIZE();
|
||||
TfLiteRegistration Register_READ_VARIABLE();
|
||||
TfLiteRegistration Register_RELU();
|
||||
TfLiteRegistration Register_RELU6();
|
||||
TfLiteRegistration Register_RESIZE_BILINEAR();
|
||||
TfLiteRegistration Register_SELECT_V2();
|
||||
TfLiteRegistration Register_SHAPE();
|
||||
TfLiteRegistration Register_SLICE();
|
||||
TfLiteRegistration Register_SPACE_TO_BATCH_ND();
|
||||
TfLiteRegistration Register_SPACE_TO_DEPTH();
|
||||
TfLiteRegistration Register_SQUARED_DIFFERENCE();
|
||||
TfLiteRegistration Register_SQUEEZE();
|
||||
TfLiteRegistration Register_SUB();
|
||||
TfLiteRegistration Register_SUM();
|
||||
TfLiteRegistration Register_SVDF();
|
||||
TfLiteRegistration Register_TRANSPOSE();
|
||||
TfLiteRegistration Register_TRANSPOSE_CONV();
|
||||
// TODO(b/230666079): resolve conflict with xtensa implementation
|
||||
TfLiteRegistration Register_UNIDIRECTIONAL_SEQUENCE_LSTM();
|
||||
TfLiteRegistration Register_VAR_HANDLE();
|
||||
TfLiteRegistration Register_WHILE();
|
||||
TfLiteRegistration Register_ZEROS_LIKE();
|
||||
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
|
||||
TfLiteRegistration Register_ABS();
|
||||
TfLiteRegistration Register_ARG_MAX();
|
||||
TfLiteRegistration Register_ARG_MIN();
|
||||
TfLiteRegistration Register_CEIL();
|
||||
TfLiteRegistration Register_CONCATENATION();
|
||||
TfLiteRegistration Register_COS();
|
||||
TfLiteRegistration Register_EQUAL();
|
||||
TfLiteRegistration Register_FLOOR();
|
||||
TfLiteRegistration Register_GREATER();
|
||||
TfLiteRegistration Register_GREATER_EQUAL();
|
||||
TfLiteRegistration Register_LESS();
|
||||
TfLiteRegistration Register_LESS_EQUAL();
|
||||
TfLiteRegistration Register_LOG();
|
||||
TfLiteRegistration Register_LOGICAL_NOT();
|
||||
TfLiteRegistration Register_MAXIMUM();
|
||||
TfLiteRegistration Register_MINIMUM();
|
||||
TfLiteRegistration Register_NEG();
|
||||
TfLiteRegistration Register_NOT_EQUAL();
|
||||
TfLiteRegistration Register_PACK();
|
||||
TfLiteRegistration Register_PAD();
|
||||
TfLiteRegistration Register_PADV2();
|
||||
TfLiteRegistration Register_RESHAPE();
|
||||
TfLiteRegistration Register_RESIZE_NEAREST_NEIGHBOR();
|
||||
TfLiteRegistration Register_ROUND();
|
||||
TfLiteRegistration Register_RSQRT();
|
||||
TfLiteRegistration Register_SIN();
|
||||
TfLiteRegistration Register_SPLIT();
|
||||
TfLiteRegistration Register_SPLIT_V();
|
||||
TfLiteRegistration Register_SQRT();
|
||||
TfLiteRegistration Register_SQUARE();
|
||||
TfLiteRegistration Register_STRIDED_SLICE();
|
||||
TfLiteRegistration Register_UNPACK();
|
||||
TfLiteRegistration Register_L2_NORMALIZATION();
|
||||
TfLiteRegistration Register_TANH();
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_MICRO_OPS_H_
|
||||
@@ -1,809 +0,0 @@
|
||||
/* 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/micro/kernels/micro_tensor_utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "fixedpoint/fixedpoint.h" // from @gemmlowp
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/internal/cppmath.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace micro_tensor_utils {
|
||||
|
||||
namespace {
|
||||
const int32_t kInt16Max = std::numeric_limits<int16_t>::max();
|
||||
const int32_t kInt16Min = std::numeric_limits<int16_t>::min();
|
||||
} // namespace
|
||||
|
||||
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||
int8_t* quantized_values, float* min_value,
|
||||
float* max_value, float* scaling_factor) {
|
||||
auto minmax = std::minmax_element(values, values + size);
|
||||
*min_value = *minmax.first;
|
||||
*max_value = *minmax.second;
|
||||
|
||||
PortableSymmetricQuantizeFloats(values, size, quantized_values, *min_value,
|
||||
*max_value, scaling_factor);
|
||||
}
|
||||
|
||||
void PortableSymmetricQuantizeFloats(const float* values, const int size,
|
||||
int8_t* quantized_values, float min_value,
|
||||
float max_value, float* scaling_factor) {
|
||||
const int32_t kScale = 127;
|
||||
const float range = std::max(std::abs(min_value), std::abs(max_value));
|
||||
if (range == 0) {
|
||||
memset(quantized_values, 0, size * sizeof(int8_t));
|
||||
*scaling_factor = 1;
|
||||
return;
|
||||
}
|
||||
*scaling_factor = range / kScale;
|
||||
const float scaling_factor_inv = kScale / range;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const int32_t quantized_value =
|
||||
static_cast<int32_t>(TfLiteRound(values[i] * scaling_factor_inv));
|
||||
// Clamp: just in case some odd numeric offset.
|
||||
quantized_values[i] = static_cast<int8_t>(
|
||||
std::min(kScale, std::max(-kScale, quantized_value)));
|
||||
}
|
||||
}
|
||||
|
||||
void PortableAsymmetricQuantizeFloats(const float* values, const int size,
|
||||
int8_t* quantized_values,
|
||||
float* scaling_factor, int32_t* offset) {
|
||||
const int32_t kMinScale = -128;
|
||||
const int32_t kMaxScale = 127;
|
||||
const double qmin_double = kMinScale;
|
||||
const double qmax_double = kMaxScale;
|
||||
const auto minmax = std::minmax_element(values, values + size);
|
||||
const double rmin = static_cast<double>(std::min(0.0f, *minmax.first));
|
||||
const double rmax = static_cast<double>(std::max(0.0f, *minmax.second));
|
||||
if (rmin == rmax) {
|
||||
memset(quantized_values, 0, size * sizeof(int8_t));
|
||||
*scaling_factor = 1;
|
||||
*offset = 0;
|
||||
return;
|
||||
} else {
|
||||
double scale = (rmax - rmin) / (qmax_double - qmin_double);
|
||||
const double zero_point_from_min = qmin_double - rmin / scale;
|
||||
const double zero_point_from_max = qmax_double - rmax / scale;
|
||||
const double zero_point_from_min_error =
|
||||
std::abs(qmin_double) + std::abs(rmin / scale);
|
||||
const double zero_point_from_max_error =
|
||||
std::abs(qmax_double) + std::abs(rmax / scale);
|
||||
const double zero_point_double =
|
||||
zero_point_from_min_error < zero_point_from_max_error
|
||||
? zero_point_from_min
|
||||
: zero_point_from_max;
|
||||
int8_t nudged_zero_point = 0;
|
||||
if (zero_point_double <= qmin_double) {
|
||||
nudged_zero_point = kMinScale;
|
||||
} else if (zero_point_double >= qmax_double) {
|
||||
nudged_zero_point = kMaxScale;
|
||||
} else {
|
||||
nudged_zero_point = static_cast<int8_t>(round(zero_point_double));
|
||||
}
|
||||
*scaling_factor = scale;
|
||||
*offset = nudged_zero_point;
|
||||
}
|
||||
const float scaling_factor_inv = 1.0f / *scaling_factor;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const int32_t quantized_value = static_cast<int32_t>(
|
||||
TfLiteRound(*offset + values[i] * scaling_factor_inv));
|
||||
quantized_values[i] =
|
||||
std::min(kMaxScale, std::max(kMinScale, quantized_value));
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiplyAccumulate(const float* matrix,
|
||||
int m_rows, int m_cols,
|
||||
const float* vector,
|
||||
int n_batch, float* result) {
|
||||
float* result_in_batch = result;
|
||||
for (int b = 0; b < n_batch; b++) {
|
||||
const float* matrix_ptr = matrix;
|
||||
for (int r = 0; r < m_rows; r++) {
|
||||
float dot_prod = 0.0f;
|
||||
const float* vector_in_batch = vector + b * m_cols;
|
||||
for (int c = 0; c < m_cols; c++) {
|
||||
dot_prod += *matrix_ptr++ * *vector_in_batch++;
|
||||
}
|
||||
*result_in_batch += dot_prod;
|
||||
++result_in_batch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||
int n_batch, float* __restrict__ result) {
|
||||
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||
const float batch_scaling_factor = scaling_factors[batch];
|
||||
// Get the address of the first row.
|
||||
const int8_t* row_ptr = matrix;
|
||||
for (int row = 0; row < m_rows; ++row) {
|
||||
// Initialize the dot product sum for the row to 0.
|
||||
int32_t dotprod = 0;
|
||||
// TODO(b/230666277): remove this
|
||||
#if defined(__GNUC__)
|
||||
// Prefetch the row to cache.
|
||||
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||
3 /* temporal locality */);
|
||||
#endif
|
||||
for (int col = 0; col < m_cols; ++col, ++row_ptr) {
|
||||
dotprod += (*row_ptr) * (vectors[col]);
|
||||
} // for col
|
||||
*result += dotprod * batch_scaling_factor;
|
||||
++result;
|
||||
} // for row
|
||||
} // for batch
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||
const int8_t* __restrict__ matrix, const int m_rows, const int m_cols,
|
||||
const int8_t* __restrict__ vectors, const float* scaling_factors,
|
||||
int n_batch, float* __restrict__ result, const float* per_channel_scale,
|
||||
const int32_t* input_offset, int32_t* scratch, int32_t* row_sums,
|
||||
bool* compute_row_sums, CpuBackendContext* context) {
|
||||
if (input_offset == nullptr) {
|
||||
PortableMatrixBatchVectorMultiplyAccumulate(
|
||||
matrix, m_rows, m_cols, vectors, scaling_factors, n_batch, result);
|
||||
return;
|
||||
}
|
||||
if (!compute_row_sums || *compute_row_sums) {
|
||||
PortableReductionSumVector(matrix, row_sums, m_rows, m_cols);
|
||||
if (compute_row_sums) {
|
||||
*compute_row_sums = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||
const float batch_scaling_factor = scaling_factors[batch];
|
||||
const int32_t batch_offset = input_offset[batch];
|
||||
const int8_t* row_ptr = matrix;
|
||||
for (int row = 0; row < m_rows; ++row) {
|
||||
int32_t dotprod = 0;
|
||||
float scale = batch_scaling_factor;
|
||||
if (per_channel_scale) {
|
||||
scale *= per_channel_scale[row];
|
||||
}
|
||||
#if defined(__GNUC__)
|
||||
// Prefetch the row to cache.
|
||||
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||
3 /* temporal locality */);
|
||||
#endif
|
||||
for (int col = 0; col < m_cols; ++col, ++row_ptr) {
|
||||
dotprod += (*row_ptr) * vectors[col];
|
||||
} // for col
|
||||
dotprod -= row_sums[row] * batch_offset;
|
||||
*result += dotprod * scale;
|
||||
++result;
|
||||
} // for row
|
||||
} // for batch
|
||||
}
|
||||
|
||||
void PortableSparseMatrixBatchVectorMultiplyAccumulate1x4(
|
||||
const float* __restrict__ matrix, const int32_t* __restrict__ segments,
|
||||
const int32_t* __restrict__ indices, int m_rows, int m_cols,
|
||||
const float* __restrict__ vector, int n_batch, float* __restrict__ result) {
|
||||
const int kBlockSize = 4;
|
||||
TFLITE_DCHECK_EQ(m_cols % kBlockSize, 0);
|
||||
for (int batch = 0; batch < n_batch; batch++) {
|
||||
const float* matrix_ptr = matrix;
|
||||
for (int row = 0; row < m_rows; row++) {
|
||||
float dot_prod = 0.0f;
|
||||
const float* vector_in_batch = vector + batch * m_cols;
|
||||
for (int i = segments[row]; i < segments[row + 1]; i++) {
|
||||
const int block_start_index = indices[i] * kBlockSize;
|
||||
const float* vector_block_in_batch_ptr =
|
||||
vector_in_batch + block_start_index;
|
||||
for (int c = 0; c < kBlockSize; c++) {
|
||||
dot_prod += *matrix_ptr++ * *vector_block_in_batch_ptr++;
|
||||
}
|
||||
}
|
||||
result[batch * m_rows + row] += dot_prod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableSparseMatrixBatchVectorMultiplyAccumulate1x16(
|
||||
const int8_t* __restrict__ matrix, const int32_t* __restrict__ segments,
|
||||
const int32_t* __restrict__ indices, int m_rows, int m_cols,
|
||||
const int8_t* __restrict__ vector, const int32_t* __restrict__ bias_vector,
|
||||
int n_batch, const int32_t input_offset, const int32_t output_multiplier,
|
||||
const int32_t output_shift, const int32_t output_offset,
|
||||
const int32_t output_activation_min, const int32_t output_activation_max,
|
||||
int8_t* __restrict__ result) {
|
||||
const int kBlockSize = 16;
|
||||
TFLITE_DCHECK_EQ(m_cols % kBlockSize, 0);
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
const int8_t* matrix_ptr = matrix;
|
||||
for (int row = 0; row < m_rows; ++row) {
|
||||
int32_t dot_prod = 0;
|
||||
const int8_t* vector_in_batch = vector + batch * m_cols;
|
||||
for (int i = segments[row]; i < segments[row + 1]; ++i) {
|
||||
const int block_start_index = indices[i] * kBlockSize;
|
||||
const int8_t* vector_block_in_batch_ptr =
|
||||
vector_in_batch + block_start_index;
|
||||
for (int c = 0; c < kBlockSize; c++) {
|
||||
dot_prod += *matrix_ptr * *vector_block_in_batch_ptr++;
|
||||
dot_prod += *matrix_ptr++ * input_offset;
|
||||
}
|
||||
}
|
||||
const int32_t bias_value = bias_vector != nullptr ? bias_vector[row] : 0;
|
||||
dot_prod = MultiplyByQuantizedMultiplier(dot_prod + bias_value,
|
||||
output_multiplier, output_shift);
|
||||
dot_prod += output_offset;
|
||||
result[batch * m_rows + row] =
|
||||
static_cast<int8_t>(ActivationFunctionWithMinMax(
|
||||
dot_prod, output_activation_min, output_activation_max));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||
const float* __restrict__ matrix, const uint8_t* __restrict__ ledger,
|
||||
int m_rows, int m_cols, const float* __restrict__ vector, int n_batch,
|
||||
float* __restrict__ result) {
|
||||
const int kBlockSize = 16;
|
||||
TFLITE_DCHECK_EQ( // NOLINT
|
||||
m_cols % kBlockSize, 0);
|
||||
for (int batch = 0; batch < n_batch; batch++) {
|
||||
const float* matrix_ptr = matrix;
|
||||
const uint8_t* ledger_ptr = ledger;
|
||||
for (int row = 0; row < m_rows; row++) {
|
||||
float dot_prod = 0.0f;
|
||||
int num_nonzero_blocks = *ledger_ptr++;
|
||||
if (num_nonzero_blocks > 0) {
|
||||
const float* vector_in_batch = vector + batch * m_cols;
|
||||
for (int i = 0; i < num_nonzero_blocks; i++) {
|
||||
const int block_start_index = *ledger_ptr++ * kBlockSize;
|
||||
const float* vector_block_in_batch_ptr =
|
||||
vector_in_batch + block_start_index;
|
||||
for (int c = 0; c < kBlockSize; c++) {
|
||||
dot_prod += *matrix_ptr++ * *vector_block_in_batch_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
result[batch * m_rows + row] += dot_prod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableSparseMatrixBatchVectorMultiplyAccumulate(
|
||||
const int8_t* __restrict__ matrix, const uint8_t* ledger, const int m_rows,
|
||||
const int m_cols, const int8_t* __restrict__ vectors,
|
||||
const float* scaling_factors, int n_batch, float* __restrict__ result) {
|
||||
static const int kBlockSize = 16;
|
||||
TFLITE_DCHECK_EQ( // NOLINT
|
||||
m_cols % kBlockSize, 0);
|
||||
for (int batch = 0; batch < n_batch; ++batch, vectors += m_cols) {
|
||||
const float batch_scaling_factor = scaling_factors[batch];
|
||||
const uint8_t* ledger_ptr = ledger;
|
||||
// Get the address of the first row.
|
||||
const int8_t* row_ptr = matrix;
|
||||
for (int row = 0; row < m_rows; ++row) {
|
||||
// Initialize the dot product sum for the row to 0.
|
||||
int32_t dotprod = 0;
|
||||
#if defined(__GNUC__)
|
||||
// Prefetch the row to cache.
|
||||
__builtin_prefetch(row_ptr, 0 /* prefetch for read */,
|
||||
3 /* temporal locality */);
|
||||
#endif
|
||||
int num_nonzero_blocks = *ledger_ptr++;
|
||||
for (int i = 0; i < num_nonzero_blocks; i++) {
|
||||
const int block_start_index = *ledger_ptr++ * kBlockSize;
|
||||
const int8_t* vector_block_ptr = vectors + block_start_index;
|
||||
for (int c = 0; c < kBlockSize; c++) {
|
||||
dotprod += (*row_ptr++) * (*vector_block_ptr++);
|
||||
} // for block
|
||||
} // for num_nonzero_blocks
|
||||
result[batch * m_rows + row] += dotprod * batch_scaling_factor;
|
||||
} // for row
|
||||
} // for batch
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||
const int8_t* input, const int32_t* bias,
|
||||
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||
T* output) {
|
||||
const int16_t output_max = std::numeric_limits<T>::max();
|
||||
const int16_t output_min = std::numeric_limits<T>::min();
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int row = 0; row < n_output; ++row) {
|
||||
int32_t acc = bias[row];
|
||||
for (int col = 0; col < n_input; ++col) {
|
||||
int8_t input_val = input[batch * n_input + col];
|
||||
int8_t weights_val = input_to_gate_weights[row * n_input + col];
|
||||
acc += input_val * weights_val;
|
||||
}
|
||||
acc = MultiplyByQuantizedMultiplier(acc, multiplier, shift);
|
||||
acc += output_zp;
|
||||
acc += output[batch * n_output + row];
|
||||
if (acc > output_max) {
|
||||
acc = output_max;
|
||||
}
|
||||
if (acc < output_min) {
|
||||
acc = output_min;
|
||||
}
|
||||
output[batch * n_output + row] = static_cast<T>(acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||
const int8_t* input, const int32_t* bias,
|
||||
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||
int32_t* scratch, int16_t* output, CpuBackendContext* context) {
|
||||
PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||
input, bias, input_to_gate_weights, multiplier, shift, n_batch, n_input,
|
||||
n_output, output_zp, output);
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiplyAccumulate(
|
||||
const int8_t* input, const int32_t* bias,
|
||||
const int8_t* input_to_gate_weights, int32_t multiplier, int32_t shift,
|
||||
int32_t n_batch, int32_t n_input, int32_t n_output, int32_t output_zp,
|
||||
int32_t* scratch, int8_t* output, CpuBackendContext* context) {
|
||||
PortableMatrixBatchVectorMultiplyAccumulateImpl(
|
||||
input, bias, input_to_gate_weights, multiplier, shift, n_batch, n_input,
|
||||
n_output, output_zp, output);
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiply(const int8_t* input,
|
||||
int32_t input_zeropoint,
|
||||
const int8_t* input_to_gate_weights,
|
||||
int32_t input_to_gate_effective_scale_a,
|
||||
int32_t input_to_gate_effective_scale_b,
|
||||
int32_t n_batch, int32_t n_input,
|
||||
int32_t n_cell, int8_t* gate_output,
|
||||
int8_t gate_output_zp) {
|
||||
const int32_t int8_max = std::numeric_limits<int8_t>::max();
|
||||
const int32_t int8_min = std::numeric_limits<int8_t>::min();
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int row = 0; row < n_cell; ++row) {
|
||||
int32_t acc = 0;
|
||||
for (int col = 0; col < n_input; ++col) {
|
||||
int32_t input_val = input[batch * n_input + col];
|
||||
int8_t weights_val = input_to_gate_weights[row * n_input + col];
|
||||
acc += (input_val - input_zeropoint) * weights_val;
|
||||
}
|
||||
acc = MultiplyByQuantizedMultiplier(acc, input_to_gate_effective_scale_a,
|
||||
input_to_gate_effective_scale_b);
|
||||
acc += gate_output_zp;
|
||||
if (acc > int8_max) {
|
||||
acc = int8_max;
|
||||
}
|
||||
if (acc < int8_min) {
|
||||
acc = int8_min;
|
||||
}
|
||||
gate_output[batch * n_cell + row] = static_cast<int8_t>(acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMatrixBatchVectorMultiply(
|
||||
const int16_t* hidden, const int8_t* hidden_to_output_weights,
|
||||
int32_t proj_effective_scale_a, int32_t proj_effective_scale_b,
|
||||
const int32_t* gate_bias, int32_t n_batch, int32_t n_hidden,
|
||||
int32_t n_output, int32_t output_zp, int8_t* proj_output) {
|
||||
const int16_t int8_max = std::numeric_limits<int8_t>::max();
|
||||
const int16_t int8_min = std::numeric_limits<int8_t>::min();
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int row = 0; row < n_output; ++row) {
|
||||
int64_t acc = gate_bias[row];
|
||||
for (int col = 0; col < n_hidden; ++col) {
|
||||
int16_t input_val = hidden[batch * n_hidden + col];
|
||||
int8_t weights_val = hidden_to_output_weights[row * n_hidden + col];
|
||||
int64_t curr = acc;
|
||||
acc += input_val * weights_val;
|
||||
if (input_val * weights_val > 0 && acc < curr) {
|
||||
acc = std::numeric_limits<int32_t>::max();
|
||||
}
|
||||
if (input_val * weights_val < 0 && acc > curr) {
|
||||
acc = std::numeric_limits<int32_t>::min();
|
||||
}
|
||||
}
|
||||
acc = MultiplyByQuantizedMultiplier(acc, proj_effective_scale_a,
|
||||
proj_effective_scale_b);
|
||||
acc += output_zp;
|
||||
if (acc > int8_max) {
|
||||
acc = int8_max;
|
||||
}
|
||||
if (acc < int8_min) {
|
||||
acc = int8_min;
|
||||
}
|
||||
proj_output[batch * n_output + row] = acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableApplyLayerNorm(const int16_t* input,
|
||||
const int16_t* layer_norm_weights,
|
||||
const int32_t* bias, int32_t layer_norm_scale_a,
|
||||
int32_t layer_norm_scale_b, int32_t variance_limit,
|
||||
int n_batch, int n_input, int16_t* output) {
|
||||
// The square of std::pow(2, 10), which is the extra factor that makes sure
|
||||
// normalized values has enough resolution.
|
||||
static const int kTwoToPower20 = 1 << 20;
|
||||
for (int i = 0; i < n_batch; ++i) {
|
||||
int64_t sum = 0;
|
||||
int64_t sum_sq = 0;
|
||||
for (int j = 0; j < n_input; ++j) {
|
||||
const int32_t index = i * n_input + j;
|
||||
int32_t val = static_cast<int32_t>(input[index]);
|
||||
sum += val;
|
||||
sum_sq += val * val;
|
||||
}
|
||||
int32_t mean =
|
||||
static_cast<int32_t>(static_cast<int64_t>(sum) * 1024 / n_input);
|
||||
// TODO(b/173994730): Avoids overflow but only works for POT n_input.
|
||||
int32_t temp = kTwoToPower20 / n_input;
|
||||
int64_t variance =
|
||||
sum_sq * temp - static_cast<int64_t>(mean) * static_cast<int64_t>(mean);
|
||||
int32_t variance2 = static_cast<int32_t>(variance / kTwoToPower20);
|
||||
if (variance2 < 1) {
|
||||
variance2 = variance_limit;
|
||||
}
|
||||
int32_t stddev_inverse_a;
|
||||
int stddev_inverse_b;
|
||||
GetInvSqrtQuantizedMultiplierExp(variance2, /*reverse_shift*/ -1,
|
||||
&stddev_inverse_a, &stddev_inverse_b);
|
||||
|
||||
for (int j = 0; j < n_input; ++j) {
|
||||
const int32_t index = i * n_input + j;
|
||||
int32_t val = static_cast<int32_t>(input[index]);
|
||||
int32_t shifted = 1024 * val - mean;
|
||||
int32_t rescaled = MultiplyByQuantizedMultiplier(
|
||||
shifted, stddev_inverse_a, stddev_inverse_b);
|
||||
int64_t val3 = rescaled * layer_norm_weights[j] + bias[j];
|
||||
int32_t val4 =
|
||||
static_cast<int32_t>((val3 > 0 ? val3 + 512 : val3 - 512) / 1024);
|
||||
int32_t val5 = MultiplyByQuantizedMultiplier(val4, layer_norm_scale_a,
|
||||
layer_norm_scale_b + 12);
|
||||
val5 = std::min(std::max(kInt16Min, val5), kInt16Max);
|
||||
output[index] = static_cast<int16_t>(val5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableApplyLayerNormFloat(const int16_t* input,
|
||||
const int16_t* layer_norm_weights,
|
||||
int32_t layer_norm_scale_a,
|
||||
int32_t layer_norm_scale_b,
|
||||
const int32_t* bias, int n_batch, int n_input,
|
||||
int16_t* output) {
|
||||
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||
const float layer_norm_scale =
|
||||
layer_norm_scale_a *
|
||||
std::pow(2.0, static_cast<double>(layer_norm_scale_b - 31));
|
||||
const float bias_scale =
|
||||
static_cast<float>(std::pow(2.0, -10)) * layer_norm_scale;
|
||||
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
float sum = 0.0f;
|
||||
float sum_sq = 0.0f;
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const float value = static_cast<float>(input[index]);
|
||||
sum += value;
|
||||
sum_sq += value * value;
|
||||
}
|
||||
const float mean = sum / n_input;
|
||||
float stddev_inv = 0.0f;
|
||||
const float variance = sum_sq / n_input - mean * mean;
|
||||
if (variance == 0) {
|
||||
stddev_inv = 1.0f / std::sqrt(1e-8f);
|
||||
} else {
|
||||
stddev_inv = 1.0f / std::sqrt(variance);
|
||||
}
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const float normalized_value =
|
||||
(static_cast<float>(input[index]) - mean) * stddev_inv;
|
||||
const float weighted_normalized_value =
|
||||
normalized_value * layer_norm_weights[i] * layer_norm_scale +
|
||||
bias[i] * bias_scale;
|
||||
const int32_t quant_output = static_cast<int32_t>(round(
|
||||
weighted_normalized_value * static_cast<float>(std::pow(2, 12))));
|
||||
output[index] = std::min(int16_max, std::max(int16_min, quant_output));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMatrixScalarMultiplyAccumulate(const int8_t* matrix,
|
||||
int32_t scalar, int32_t n_row,
|
||||
int32_t n_col, int32_t* output) {
|
||||
for (int i = 0; i < n_row; ++i) {
|
||||
int32_t row_sum = 0;
|
||||
for (int j = 0; j < n_col; ++j) {
|
||||
row_sum += *matrix++;
|
||||
}
|
||||
output[i] += row_sum * scalar;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableApplySigmoid(const int16_t* input, int32_t n_batch,
|
||||
int32_t n_input, int16_t* output) {
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int c = 0; c < n_input; c++) {
|
||||
using F3 = gemmlowp::FixedPoint<std::int16_t, 3>;
|
||||
using F0 = gemmlowp::FixedPoint<std::int16_t, 0>;
|
||||
const int index = batch * n_input + c;
|
||||
F3 sigmoid_input = F3::FromRaw(input[index]);
|
||||
F0 sigmoid_output = gemmlowp::logistic(sigmoid_input);
|
||||
output[index] = sigmoid_output.raw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableApplySigmoidFloat(const int16_t* input, int32_t n_batch,
|
||||
int32_t n_input, int16_t* output) {
|
||||
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const float float_input =
|
||||
input[index] * static_cast<float>(std::pow(2, -12));
|
||||
const float float_output = 1.0f / (1.0f + std::exp(-float_input));
|
||||
const int32_t quant_output = static_cast<int32_t>(
|
||||
float_output * static_cast<float>(std::pow(2, 15)));
|
||||
const int32_t quant_output_clamped =
|
||||
std::min(int16_max, std::max(int16_min, quant_output));
|
||||
output[index] = static_cast<int16_t>(quant_output_clamped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <int IntegerBits>
|
||||
void PortableApplyTanhImpl(const int16_t* input, int32_t n_batch,
|
||||
int32_t n_input, int16_t* output) {
|
||||
using FX = gemmlowp::FixedPoint<std::int16_t, IntegerBits>;
|
||||
using F0 = gemmlowp::FixedPoint<std::int16_t, 0>;
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
FX tanh_input = FX::FromRaw(input[index]);
|
||||
F0 tanh_output = gemmlowp::tanh(tanh_input);
|
||||
output[index] = tanh_output.raw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableApplyTanh(int32_t integer_bits, const int16_t* input,
|
||||
int32_t n_batch, int32_t n_input, int16_t* output) {
|
||||
if (integer_bits > 6) {
|
||||
TFLITE_ASSERT_FALSE;
|
||||
}
|
||||
#define DISPATCH_TANH(i) \
|
||||
case i: \
|
||||
PortableApplyTanhImpl<i>(input, n_batch, n_input, output); \
|
||||
break;
|
||||
switch (integer_bits) {
|
||||
DISPATCH_TANH(0);
|
||||
DISPATCH_TANH(1);
|
||||
DISPATCH_TANH(2);
|
||||
DISPATCH_TANH(3);
|
||||
DISPATCH_TANH(4);
|
||||
DISPATCH_TANH(5);
|
||||
DISPATCH_TANH(6);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
#undef DISPATCH_TANH
|
||||
}
|
||||
|
||||
void PortableApplyTanhFloat(const int16_t* input, int32_t n_batch,
|
||||
int32_t n_input, int32_t integer_bits,
|
||||
int16_t* output) {
|
||||
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||
const double two = 2.0;
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const float float_input =
|
||||
input[index] * std::pow(two, static_cast<double>(integer_bits));
|
||||
const float float_output = std::tanh(float_input);
|
||||
const int32_t quant_output = static_cast<int32_t>(
|
||||
float_output * static_cast<float>(std::pow(2, 15)));
|
||||
const int32_t quant_output_clamped =
|
||||
std::min(int16_max, std::max(int16_min, quant_output));
|
||||
output[index] = static_cast<int16_t>(quant_output_clamped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||
int n_batch, int n_input, int shift, int16_t* output) {
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const int16_t a = input_1[index];
|
||||
const int16_t b = input_2[index];
|
||||
const int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
|
||||
output[index] =
|
||||
static_cast<int16_t>(gemmlowp::RoundingDivideByPOT(value, shift));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
|
||||
int32_t multiplier, int32_t shift, int32_t n_batch,
|
||||
int32_t n_input, int32_t output_zp, int8_t* output) {
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
const int16_t a = input_1[index];
|
||||
const int16_t b = input_2[index];
|
||||
int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
|
||||
value = MultiplyByQuantizedMultiplier(value, multiplier, shift);
|
||||
value += output_zp;
|
||||
value = std::min(std::max(static_cast<int32_t>(-128), value),
|
||||
static_cast<int32_t>(127));
|
||||
|
||||
output[index] = static_cast<int8_t>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableCwiseAdd(const int16_t* input_1, const int16_t* input_2,
|
||||
int n_batch, int n_input, int16_t* output) {
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
for (int i = 0; i < n_input; ++i) {
|
||||
const int index = batch * n_input + i;
|
||||
int32_t sum = input_1[index] + input_2[index];
|
||||
const int32_t sum_clamped = std::min(kInt16Max, std::max(kInt16Min, sum));
|
||||
output[index] = static_cast<int16_t>(sum_clamped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float PortableVectorVectorDotProduct(const float* vector1, const float* vector2,
|
||||
int v_size) {
|
||||
float result = 0.0;
|
||||
for (int v = 0; v < v_size; v++) {
|
||||
result += *vector1++ * *vector2++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
inline int32_t VectorVectorDotProduct(const int16_t* vector1,
|
||||
const int16_t* vector2, int v_size) {
|
||||
int32_t result = 0;
|
||||
for (int v = 0; v < v_size; v++) {
|
||||
result += *vector1++ * *vector2++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void PortableBatchVectorBatchVectorDotProduct(const int16_t* vector1,
|
||||
const int16_t* vector2,
|
||||
int v_size, int n_batch,
|
||||
int32_t* result) {
|
||||
for (int b = 0; b < n_batch; b++) {
|
||||
result[b] = VectorVectorDotProduct(vector1, vector2, v_size);
|
||||
vector1 += v_size;
|
||||
vector2 += v_size;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableVectorBatchVectorCwiseProductAccumulate(
|
||||
const int16_t* vector, int v_size, const int16_t* batch_vector, int n_batch,
|
||||
int32_t multiplier, int shift, int16_t* result) {
|
||||
for (int b = 0; b < n_batch; b++) {
|
||||
for (int v = 0; v < v_size; v++) {
|
||||
int32_t prod = vector[v] * *batch_vector++;
|
||||
prod = MultiplyByQuantizedMultiplier(prod, multiplier, shift);
|
||||
int32_t output = prod + *result;
|
||||
output = std::max(std::min(static_cast<int32_t>(32767), output),
|
||||
static_cast<int32_t>(-32768));
|
||||
*result++ = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PortableSub1Vector(const float* vector, int v_size, float* result) {
|
||||
for (int v = 0; v < v_size; v++) {
|
||||
*result++ = 1.0f - *vector++;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableSub1Vector(const int16_t* vector, int v_size, int16_t* result) {
|
||||
static const int16_t kOne = 32767;
|
||||
for (int v = 0; v < v_size; v++) {
|
||||
*result++ = kOne - *vector++;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableVectorScalarMultiply(const int8_t* vector, const int v_size,
|
||||
const float scale, float* result) {
|
||||
for (int v = 0; v < v_size; ++v) {
|
||||
*result++ = scale * *vector++;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableMeanStddevNormalization(const float* __restrict__ input_vector,
|
||||
float* __restrict__ output_vector,
|
||||
int v_size, int n_batch) {
|
||||
for (int batch = 0; batch < n_batch; ++batch) {
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < v_size; ++i) {
|
||||
sum += input_vector[i];
|
||||
}
|
||||
const float mean = sum / v_size;
|
||||
float sum_diff_sq = 0.0f;
|
||||
for (int i = 0; i < v_size; ++i) {
|
||||
const float diff = input_vector[i] - mean;
|
||||
sum_diff_sq += diff * diff;
|
||||
}
|
||||
const float variance = sum_diff_sq / v_size;
|
||||
constexpr float kNormalizationConstant = 1e-8f;
|
||||
const float stddev_inv =
|
||||
1.0f / std::sqrt(variance + kNormalizationConstant);
|
||||
for (int i = 0; i < v_size; ++i) {
|
||||
output_vector[i] = (input_vector[i] - mean) * stddev_inv;
|
||||
}
|
||||
input_vector += v_size;
|
||||
output_vector += v_size;
|
||||
}
|
||||
}
|
||||
|
||||
void PortableTwoGateSaturatingAdd(const int8_t* input, int8_t input_zp,
|
||||
const int8_t* recurrent, int8_t recurrent_zp,
|
||||
int32_t input_effective_scale_a,
|
||||
int32_t input_effective_scale_b,
|
||||
int32_t recurrent_effective_scale_a,
|
||||
int32_t recurrent_effective_scale_b,
|
||||
int32_t n_batch, int32_t n_cell,
|
||||
int16_t* output) {
|
||||
const int32_t int16_max = std::numeric_limits<int16_t>::max();
|
||||
const int32_t int16_min = std::numeric_limits<int16_t>::min();
|
||||
for (int i = 0; i < n_batch * n_cell; ++i) {
|
||||
int32_t x = static_cast<int32_t>(input[i]) - static_cast<int32_t>(input_zp);
|
||||
int32_t h =
|
||||
static_cast<int32_t>(recurrent[i]) - static_cast<int32_t>(recurrent_zp);
|
||||
int32_t x_scaled = MultiplyByQuantizedMultiplier(x, input_effective_scale_a,
|
||||
input_effective_scale_b);
|
||||
int32_t h_scaled = MultiplyByQuantizedMultiplier(
|
||||
h, recurrent_effective_scale_a, recurrent_effective_scale_b);
|
||||
int32_t y = h_scaled + x_scaled;
|
||||
if (y > int16_max) {
|
||||
y = int16_max;
|
||||
}
|
||||
if (y < int16_min) {
|
||||
y = int16_min;
|
||||
}
|
||||
output[i] = static_cast<int16_t>(y);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace micro_tensor_utils
|
||||
} // namespace tflite
|
||||
@@ -1,74 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
extern const int kMulInput1Tensor;
|
||||
extern const int kMulInput2Tensor;
|
||||
extern const int kMulOutputTensor;
|
||||
|
||||
struct OpDataMul {
|
||||
int32_t input1_zero_point;
|
||||
int32_t input2_zero_point;
|
||||
|
||||
int32_t output_activation_min;
|
||||
int32_t output_activation_max;
|
||||
int32_t output_zero_point;
|
||||
int32_t output_multiplier;
|
||||
int output_shift;
|
||||
|
||||
float output_activation_min_f32;
|
||||
float output_activation_max_f32;
|
||||
};
|
||||
|
||||
void* MulInit(TfLiteContext* context, const char* buffer, size_t length);
|
||||
|
||||
TfLiteStatus CalculateOpDataMul(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteMulParams* params, OpDataMul* data);
|
||||
|
||||
TfLiteStatus MulPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
void EvalMulQuantizedReference(TfLiteContext* context, TfLiteNode* node,
|
||||
const OpDataMul* data,
|
||||
const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
void EvalMulFloatReference(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteMulParams* params, const OpDataMul* data,
|
||||
const TfLiteEvalTensor* input1,
|
||||
const TfLiteEvalTensor* input2,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
// Generic must define registration function.
|
||||
TfLiteRegistration Register_MUL();
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_MUL_INT8();
|
||||
#else
|
||||
// Fallback registration
|
||||
inline TfLiteRegistration Register_MUL_INT8() { return Register_MUL(); }
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_
|
||||
@@ -1,59 +0,0 @@
|
||||
/* 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/kernels/internal/reference/neg.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace neg {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
switch (input->type) {
|
||||
// TODO(wangtz): handle for kTfLiteInt8
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::Negate(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace neg
|
||||
|
||||
TfLiteRegistration Register_NEG() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, neg::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,116 +0,0 @@
|
||||
/* 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace pack {
|
||||
namespace {
|
||||
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus PackImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLiteEvalTensor* output, int values_count, int axis) {
|
||||
const TfLiteEvalTensor* input0 =
|
||||
tflite::micro::GetEvalInput(context, node, 0);
|
||||
|
||||
const int dimensions = output->dims->size;
|
||||
const TfLiteIntArray* input_dims = input0->dims;
|
||||
const TfLiteIntArray* output_dims = output->dims;
|
||||
|
||||
if (axis < 0) {
|
||||
axis += dimensions;
|
||||
}
|
||||
|
||||
int outer_size = 1;
|
||||
for (int i = 0; i < axis; ++i) {
|
||||
outer_size *= output_dims->data[i];
|
||||
}
|
||||
int copy_size = 1;
|
||||
for (int i = axis + 1; i < dimensions; ++i) {
|
||||
copy_size *= output_dims->data[i];
|
||||
}
|
||||
int input_size = 1;
|
||||
for (int i = 0; i < input_dims->size; ++i) {
|
||||
input_size *= input_dims->data[i];
|
||||
}
|
||||
TFLITE_DCHECK_EQ(input_size, copy_size * outer_size);
|
||||
|
||||
T* output_data = tflite::micro::GetTensorData<T>(output);
|
||||
|
||||
for (int i = 0; i < values_count; ++i) {
|
||||
const TfLiteEvalTensor* t = tflite::micro::GetEvalInput(context, node, i);
|
||||
const T* input_data = tflite::micro::GetTensorData<T>(t);
|
||||
for (int k = 0; k < outer_size; ++k) {
|
||||
const T* input_ptr = input_data + copy_size * k;
|
||||
int loc = k * values_count * copy_size + i * copy_size;
|
||||
T* output_ptr = output_data + loc;
|
||||
for (int j = 0; j < copy_size; ++j) output_ptr[j] = input_ptr[j];
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLitePackParams* data =
|
||||
reinterpret_cast<TfLitePackParams*>(node->builtin_data);
|
||||
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (output->type) {
|
||||
case kTfLiteFloat32: {
|
||||
return PackImpl<float>(context, node, output, data->values_count,
|
||||
data->axis);
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
return PackImpl<int8_t>(context, node, output, data->values_count,
|
||||
data->axis);
|
||||
}
|
||||
case kTfLiteInt32: {
|
||||
return PackImpl<int32_t>(context, node, output, data->values_count,
|
||||
data->axis);
|
||||
}
|
||||
case kTfLiteInt64: {
|
||||
return PackImpl<int64_t>(context, node, output, data->values_count,
|
||||
data->axis);
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Type '%s' is not supported by pack.",
|
||||
TfLiteTypeGetName(output->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace pack
|
||||
|
||||
TfLiteRegistration Register_PACK() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, pack::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,236 +0,0 @@
|
||||
/* 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/kernels/internal/reference/pad.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/portable_tensor.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace pad {
|
||||
namespace {
|
||||
|
||||
struct OpData {
|
||||
PadParams params;
|
||||
int32_t output_zero_point;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE(context, NumInputs(node) == 2 || NumInputs(node) == 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, /*index=*/0);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* paddings =
|
||||
micro_context->AllocateTempInputTensor(node, /*index=*/1);
|
||||
TF_LITE_ENSURE(context, paddings != nullptr);
|
||||
TfLiteTensor* constant_values =
|
||||
NumInputs(node) == 3
|
||||
? micro_context->AllocateTempInputTensor(node, /*index=*/2)
|
||||
: nullptr;
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, /*index=*/0);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
|
||||
// Current implementations rely on the inputs being <= 4D.
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) <=
|
||||
reference_ops::PadKernelMaxDimensionCount());
|
||||
|
||||
if (constant_values != nullptr) {
|
||||
TF_LITE_ENSURE_EQ(context, input->type, constant_values->type);
|
||||
// Ensure that constant_values is a scalar.
|
||||
TF_LITE_ENSURE_EQ(context, NumElements(constant_values), 1);
|
||||
}
|
||||
|
||||
// There must be a pair of paddings for each output dimension.
|
||||
TF_LITE_ENSURE_EQ(context, GetTensorShape(paddings).FlatSize(),
|
||||
output->dims->size * 2);
|
||||
|
||||
// On Micro, outputs must be properly sized by the converter.
|
||||
// NOTE: This data is only available because the paddings buffer is stored in
|
||||
// the flatbuffer:
|
||||
TF_LITE_ENSURE(context, IsConstantTensor(paddings));
|
||||
const int32_t* paddings_data = GetTensorData<int32_t>(paddings);
|
||||
for (int i = 0; i < output->dims->size; i++) {
|
||||
int output_dim = output->dims->data[i];
|
||||
int expected_dim =
|
||||
input->dims->data[i] + paddings_data[i * 2] + paddings_data[i * 2 + 1];
|
||||
TF_LITE_ENSURE_EQ(context, output_dim, expected_dim);
|
||||
}
|
||||
|
||||
// Calculate OpData:
|
||||
data->params.resizing_category = ResizingCategory::kGenericResize;
|
||||
const int paddings_total = GetTensorShape(paddings).FlatSize();
|
||||
if (paddings_total == 8 && (paddings_data[0] == 0 && paddings_data[1] == 0) &&
|
||||
(paddings_data[6] == 0 && paddings_data[7] == 0)) {
|
||||
data->params.resizing_category = ResizingCategory::kImageStyle;
|
||||
}
|
||||
|
||||
const int num_input_dimensions = NumDimensions(input);
|
||||
data->params.left_padding_count = num_input_dimensions;
|
||||
data->params.right_padding_count = num_input_dimensions;
|
||||
|
||||
for (int idx = num_input_dimensions - 1; idx >= 0; --idx) {
|
||||
data->params.left_padding[idx] = paddings_data[idx * 2];
|
||||
data->params.right_padding[idx] = paddings_data[idx * 2 + 1];
|
||||
}
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
if (constant_values == nullptr) {
|
||||
// Quantized Pad requires that 0 is represented in the quantized
|
||||
// range.
|
||||
TF_LITE_ENSURE(context, output->params.zero_point >=
|
||||
std::numeric_limits<int8_t>::min());
|
||||
TF_LITE_ENSURE(context, output->params.zero_point <=
|
||||
std::numeric_limits<int8_t>::max());
|
||||
} else {
|
||||
// Quantized Pad requires that 'constant_values' is represented in the
|
||||
// same quantized range as the input and output tensors.
|
||||
TF_LITE_ENSURE_EQ(context, output->params.zero_point,
|
||||
constant_values->params.zero_point);
|
||||
TF_LITE_ENSURE_EQ(context, static_cast<double>(output->params.scale),
|
||||
static_cast<double>(constant_values->params.scale));
|
||||
}
|
||||
data->output_zero_point = output->params.zero_point;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(paddings);
|
||||
if (constant_values != nullptr) {
|
||||
micro_context->DeallocateTempTfLiteTensor(constant_values);
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData* data = static_cast<const OpData*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, /*index=*/0);
|
||||
const TfLiteEvalTensor* constant_values =
|
||||
NumInputs(node) == 3
|
||||
? tflite::micro::GetEvalInput(context, node, /*index=*/2)
|
||||
: nullptr;
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, /*index=*/0);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
float pad_value =
|
||||
constant_values == nullptr
|
||||
? 0.f
|
||||
: *tflite::micro::GetTensorData<float>(constant_values);
|
||||
if (data->params.resizing_category == ResizingCategory::kImageStyle) {
|
||||
reference_ops::PadImageStyle(
|
||||
data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input), &pad_value,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
} else {
|
||||
reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
&pad_value, tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
}
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
int8_t pad_value;
|
||||
if (constant_values == nullptr) {
|
||||
pad_value = static_cast<uint8_t>(data->output_zero_point);
|
||||
} else {
|
||||
pad_value = *tflite::micro::GetTensorData<int8_t>(constant_values);
|
||||
}
|
||||
if (data->params.resizing_category == ResizingCategory::kImageStyle) {
|
||||
reference_ops::PadImageStyle(
|
||||
data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input), &pad_value,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
&pad_value, tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
}
|
||||
} break;
|
||||
case kTfLiteInt16: {
|
||||
int16_t pad_value =
|
||||
constant_values == nullptr
|
||||
? 0
|
||||
: *tflite::micro::GetTensorData<int16_t>(constant_values);
|
||||
reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
&pad_value, tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
} break;
|
||||
case kTfLiteInt32: {
|
||||
int32_t pad_value =
|
||||
constant_values == nullptr
|
||||
? 0
|
||||
: *tflite::micro::GetTensorData<int32_t>(constant_values);
|
||||
reference_ops::Pad(data->params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int32_t>(input),
|
||||
&pad_value, tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int32_t>(output));
|
||||
} break;
|
||||
default:
|
||||
|
||||
MicroPrintf("Type %s not currently supported by Pad.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace pad
|
||||
|
||||
TfLiteRegistration Register_PAD() {
|
||||
return tflite::micro::RegisterOp(pad::Init, pad::Prepare, pad::Eval);
|
||||
}
|
||||
|
||||
// Also register Pad as PadV2.
|
||||
TfLiteRegistration Register_PADV2() {
|
||||
return tflite::micro::RegisterOp(pad::Init, pad::Prepare, pad::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,98 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/pooling.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/pooling.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
TfLiteStatus AverageEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpDataPooling* data =
|
||||
static_cast<const OpDataPooling*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
micro::GetEvalInput(context, node, kPoolingInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
micro::GetEvalOutput(context, node, kPoolingOutputTensor);
|
||||
|
||||
// Inputs and outputs share the same type, guaranteed by the converter.
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
AveragePoolingEvalFloat(context, node, params, data, input, output);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
AveragePoolingEvalQuantized(context, node, params, data, input, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Input type %s is not currently supported",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpDataPooling* data =
|
||||
static_cast<const OpDataPooling*>(node->user_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
micro::GetEvalInput(context, node, kPoolingInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
micro::GetEvalOutput(context, node, kPoolingOutputTensor);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
MaxPoolingEvalFloat(context, node, params, data, input, output);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
MaxPoolingEvalQuantized(context, node, params, data, input, output);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s not currently supported.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpDataPooling));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_AVERAGE_POOL_2D() {
|
||||
return tflite::micro::RegisterOp(Init, PoolingPrepare, AverageEval);
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_MAX_POOL_2D() {
|
||||
return tflite::micro::RegisterOp(Init, PoolingPrepare, MaxEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,85 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_POOLING_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_POOLING_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
extern const int kPoolingInputTensor;
|
||||
extern const int kPoolingOutputTensor;
|
||||
|
||||
struct OpDataPooling {
|
||||
TfLitePaddingValues padding;
|
||||
int32_t activation_min;
|
||||
int32_t activation_max;
|
||||
float activation_min_f32;
|
||||
float activation_max_f32;
|
||||
};
|
||||
|
||||
TfLiteStatus CalculateOpDataPooling(const TfLiteContext* context,
|
||||
const TfLitePoolParams* params,
|
||||
const TfLiteTensor* input,
|
||||
const TfLiteTensor* output,
|
||||
OpDataPooling* data);
|
||||
|
||||
TfLiteStatus PoolingPrepare(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
void AveragePoolingEvalFloat(const TfLiteContext* context,
|
||||
const TfLiteNode* node,
|
||||
const TfLitePoolParams* params,
|
||||
const OpDataPooling* data,
|
||||
const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
void AveragePoolingEvalQuantized(TfLiteContext* context, const TfLiteNode* node,
|
||||
const TfLitePoolParams* params,
|
||||
const OpDataPooling* data,
|
||||
const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
void MaxPoolingEvalFloat(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLitePoolParams* params, const OpDataPooling* data,
|
||||
const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
void MaxPoolingEvalQuantized(TfLiteContext* context, TfLiteNode* node,
|
||||
TfLitePoolParams* params,
|
||||
const OpDataPooling* data,
|
||||
const TfLiteEvalTensor* input,
|
||||
TfLiteEvalTensor* output);
|
||||
|
||||
#if defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_AVERAGE_POOL_2D_INT8();
|
||||
|
||||
TfLiteRegistration Register_MAX_POOL_2D_INT8();
|
||||
#else
|
||||
inline TfLiteRegistration Register_AVERAGE_POOL_2D_INT8() {
|
||||
return tflite::Register_AVERAGE_POOL_2D();
|
||||
}
|
||||
|
||||
inline TfLiteRegistration Register_MAX_POOL_2D_INT8() {
|
||||
return tflite::Register_MAX_POOL_2D();
|
||||
}
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_POOLING_H_
|
||||
@@ -1,74 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/prelu.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/prelu.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
void* PreluInit(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(PreluParams));
|
||||
}
|
||||
|
||||
TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const PreluParams& params =
|
||||
*(static_cast<const PreluParams*>(node->user_data));
|
||||
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
const TfLiteEvalTensor* alpha = tflite::micro::GetEvalInput(context, node, 1);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
BroadcastPrelu4DSlowFloat(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(alpha),
|
||||
tflite::micro::GetTensorData<float>(alpha),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
reference_ops::BroadcastPrelu4DSlow(
|
||||
params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(alpha),
|
||||
tflite::micro::GetTensorData<int8_t>(alpha),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
default:
|
||||
MicroPrintf("Only float32 and uint8_t are supported currently, got %d.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteRegistration Register_PRELU() {
|
||||
return tflite::micro::RegisterOp(PreluInit, PreluPrepare, PreluEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,374 +0,0 @@
|
||||
/* Copyright 2022 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/mean.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/reduce.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/reduce.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
const int kMaxNumberOfAxis = 5;
|
||||
const int kMaxNumberOfReducedAxis = 2;
|
||||
|
||||
TfLiteStatus PrepareSimple(TfLiteContext* context, TfLiteNode* node,
|
||||
int32_t* multiplier, int* shift) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
// Inputs Tensor (dtype depends on quantization):
|
||||
// [0] = Input
|
||||
// [1] = Axis
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
|
||||
// Outputs Tensor (dtype depends on quantization):
|
||||
// [0] = Output
|
||||
|
||||
// Validate number of inputs and outputs
|
||||
TF_LITE_ENSURE_EQ(context, node->inputs->size, 2);
|
||||
TF_LITE_ENSURE_EQ(context, node->outputs->size, 1);
|
||||
|
||||
// Validate axis type
|
||||
TfLiteTensor* axis = micro_context->AllocateTempInputTensor(node, 1);
|
||||
TF_LITE_ENSURE(context, axis != nullptr);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, axis->type, kTfLiteInt32);
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
const double real_multiplier = static_cast<double>(input->params.scale) /
|
||||
static_cast<double>(output->params.scale);
|
||||
QuantizeMultiplier(real_multiplier, multiplier, shift);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
}
|
||||
micro_context->DeallocateTempTfLiteTensor(axis);
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus PrepareMaxHelper(TfLiteContext* context, TfLiteNode* node,
|
||||
OpDataReduce* op_data) {
|
||||
TF_LITE_ENSURE_OK(context, PrepareSimple(context, node, &op_data->multiplier,
|
||||
&op_data->shift));
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
TfLiteTensor* axis = micro_context->AllocateTempInputTensor(node, 1);
|
||||
|
||||
op_data->input_scale = input->params.scale;
|
||||
op_data->output_scale = output->params.scale;
|
||||
op_data->num_output_elements = NumElements(output);
|
||||
|
||||
context->RequestScratchBufferInArena(context, sizeof(int) * input->dims->size,
|
||||
&op_data->temp_buffer_idx);
|
||||
context->RequestScratchBufferInArena(
|
||||
context, sizeof(int) * static_cast<int>(ElementCount(*axis->dims)),
|
||||
&op_data->resolved_axis_idx);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
micro_context->DeallocateTempTfLiteTensor(axis);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus PrepareMeanOrSumHelper(TfLiteContext* context, TfLiteNode* node,
|
||||
OpDataReduce* op_data) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
TfLiteTensor* output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
if (input->type == kTfLiteInt8 || input->type == kTfLiteInt16) {
|
||||
const double real_multiplier = static_cast<double>(input->params.scale) /
|
||||
static_cast<double>(output->params.scale);
|
||||
QuantizeMultiplier(real_multiplier, &op_data->multiplier, &op_data->shift);
|
||||
}
|
||||
|
||||
int output_size = NumElements(output);
|
||||
if (input->type == kTfLiteInt8 || input->type == kTfLiteInt16) {
|
||||
context->RequestScratchBufferInArena(context, output_size * sizeof(int32_t),
|
||||
&op_data->temp_buffer_idx);
|
||||
op_data->input_zp = input->params.zero_point;
|
||||
op_data->input_scale = input->params.scale;
|
||||
op_data->output_zp = output->params.zero_point;
|
||||
op_data->output_scale = output->params.scale;
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_OK(
|
||||
context,
|
||||
PrepareSimple(context, node, &(op_data->multiplier), &(op_data->shift)));
|
||||
// TODO(b/144955155): Support uint8_t(b/144955155) and int8_t(b/144955018)
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void ResolveAxis(const int* axis_data, int axis_count,
|
||||
tflite::MeanParams* op_params) {
|
||||
int i = 0;
|
||||
for (; i < axis_count; ++i) {
|
||||
op_params->axis[i] = static_cast<int16_t>(axis_data[i]);
|
||||
}
|
||||
for (; i < 4; ++i) {
|
||||
op_params->axis[i] = 1;
|
||||
}
|
||||
op_params->axis_count = axis_count;
|
||||
}
|
||||
|
||||
TfLiteStatus EvalMeanHelper(TfLiteContext* context, TfLiteNode* node,
|
||||
OpDataReduce* op_data) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
const TfLiteEvalTensor* axis = tflite::micro::GetEvalInput(context, node, 1);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TfLiteReducerParams* params =
|
||||
reinterpret_cast<TfLiteReducerParams*>(node->builtin_data);
|
||||
|
||||
int num_axis = static_cast<int>(ElementCount(*axis->dims));
|
||||
int temp_index[kMaxNumberOfAxis];
|
||||
int resolved_axis[kMaxNumberOfReducedAxis];
|
||||
|
||||
tflite::MeanParams op_params;
|
||||
ResolveAxis(tflite::micro::GetTensorData<int>(axis), num_axis, &op_params);
|
||||
|
||||
// Special case mean implementation exists for 4D mean across axes 1 and 2.
|
||||
bool special_case_4d_axes_1_and_2 =
|
||||
input->dims->size == 4 && op_params.axis_count == 2 &&
|
||||
((op_params.axis[0] == 1 && op_params.axis[1] == 2) ||
|
||||
(op_params.axis[0] == 2 && op_params.axis[1] == 1));
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
// Defer to specialized implementation for 4D Mean across axes 1 & 2.
|
||||
if (params->keep_dims && special_case_4d_axes_1_and_2) {
|
||||
reference_ops::Mean(op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
} else {
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::Mean(
|
||||
tflite::micro::GetTensorData<float>(input), input->dims->data,
|
||||
input->dims->size, tflite::micro::GetTensorData<float>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_index, resolved_axis,
|
||||
tflite::micro::GetTensorData<float>(output)));
|
||||
}
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
// Defer to specialized implementation for 4D Mean across axes 1 & 2.
|
||||
if (params->keep_dims && special_case_4d_axes_1_and_2) {
|
||||
reference_integer_ops::Mean(
|
||||
op_params, op_data->multiplier, op_data->shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input), op_data->input_zp,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output), op_data->output_zp);
|
||||
} else if (op_data->input_zp == op_data->output_zp &&
|
||||
op_data->input_scale == op_data->output_scale) {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::Mean(
|
||||
tflite::micro::GetTensorData<int8_t>(input), input->dims->data,
|
||||
input->dims->size, tflite::micro::GetTensorData<int8_t>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_index, resolved_axis, temp_buffer));
|
||||
} else {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::QuantizedMeanOrSum(
|
||||
tflite::micro::GetTensorData<int8_t>(input), op_data->input_zp,
|
||||
op_data->input_scale, input->dims->data, input->dims->size,
|
||||
tflite::micro::GetTensorData<int8_t>(output),
|
||||
op_data->output_zp, op_data->output_scale, output->dims->data,
|
||||
output->dims->size, tflite::micro::GetTensorData<int>(axis),
|
||||
num_axis, params->keep_dims, temp_index, resolved_axis,
|
||||
temp_buffer, false));
|
||||
}
|
||||
} break;
|
||||
case kTfLiteInt16: {
|
||||
// Defer to specialized implementation for 4D Mean across axes 1 & 2.
|
||||
if (params->keep_dims && special_case_4d_axes_1_and_2) {
|
||||
reference_integer_ops::Mean(
|
||||
op_params, op_data->multiplier, op_data->shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input), op_data->input_zp,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output), op_data->output_zp);
|
||||
} else if (op_data->input_zp == op_data->output_zp &&
|
||||
op_data->input_scale == op_data->output_scale) {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::Mean(tflite::micro::GetTensorData<int16_t>(input),
|
||||
input->dims->data, input->dims->size,
|
||||
tflite::micro::GetTensorData<int16_t>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis),
|
||||
num_axis, params->keep_dims, temp_index,
|
||||
resolved_axis, temp_buffer));
|
||||
} else {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::QuantizedMeanOrSum(
|
||||
tflite::micro::GetTensorData<int16_t>(input), op_data->input_zp,
|
||||
op_data->input_scale, input->dims->data, input->dims->size,
|
||||
tflite::micro::GetTensorData<int16_t>(output),
|
||||
op_data->output_zp, op_data->output_scale, output->dims->data,
|
||||
output->dims->size, tflite::micro::GetTensorData<int>(axis),
|
||||
num_axis, params->keep_dims, temp_index, resolved_axis,
|
||||
temp_buffer, false));
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
TF_LITE_ENSURE_MSG(context, false,
|
||||
"Currently, only float32, int8 or int16 input type "
|
||||
"is supported.");
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus EvalMaxHelper(TfLiteContext* context, TfLiteNode* node,
|
||||
OpDataReduce* op_data) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
const TfLiteEvalTensor* axis = tflite::micro::GetEvalInput(context, node, 1);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
TfLiteReducerParams* params =
|
||||
static_cast<TfLiteReducerParams*>(node->builtin_data);
|
||||
|
||||
// Interpret an axis tensor with null dimensions as a scalar
|
||||
int num_axis = static_cast<int>(ElementCount(*axis->dims));
|
||||
int* temp_buffer = static_cast<int*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
int* resolved_axis = static_cast<int*>(
|
||||
context->GetScratchBuffer(context, op_data->resolved_axis_idx));
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::ReduceGeneric<float>(
|
||||
tflite::micro::GetTensorData<float>(input), input->dims->data,
|
||||
input->dims->size, tflite::micro::GetTensorData<float>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_buffer, resolved_axis,
|
||||
std::numeric_limits<float>::lowest(),
|
||||
[](const float current, const float in) -> float {
|
||||
return (in > current) ? in : current;
|
||||
}));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
TF_LITE_ENSURE_EQ(context, static_cast<double>(op_data->input_scale),
|
||||
static_cast<double>(op_data->output_scale));
|
||||
TF_LITE_ENSURE_EQ(context, op_data->input_zp, op_data->output_zp);
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::ReduceGeneric<int8_t>(
|
||||
tflite::micro::GetTensorData<int8_t>(input), input->dims->data,
|
||||
input->dims->size, tflite::micro::GetTensorData<int8_t>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_buffer, resolved_axis,
|
||||
std::numeric_limits<int8_t>::lowest(),
|
||||
[](const int8_t current, const int8_t in) -> int8_t {
|
||||
return (in > current) ? in : current;
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Only float32 and int8 types are supported.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus EvalSumHelper(TfLiteContext* context, TfLiteNode* node,
|
||||
OpDataReduce* op_data) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
const TfLiteEvalTensor* axis = tflite::micro::GetEvalInput(context, node, 1);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
TfLiteReducerParams* params =
|
||||
static_cast<TfLiteReducerParams*>(node->builtin_data);
|
||||
|
||||
// Interpret an axis tensor with null dimensions as a scalar.
|
||||
int num_axis = static_cast<int>(ElementCount(*axis->dims));
|
||||
int temp_index[kMaxNumberOfAxis];
|
||||
int resolved_axis[kMaxNumberOfReducedAxis];
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::ReduceGeneric<float>(
|
||||
tflite::micro::GetTensorData<float>(input), input->dims->data,
|
||||
input->dims->size, tflite::micro::GetTensorData<float>(output),
|
||||
output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_index, resolved_axis, /*init_value=*/0.f,
|
||||
[](const float current, const float in) -> float {
|
||||
return in + current;
|
||||
}));
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::QuantizedMeanOrSum(
|
||||
tflite::micro::GetTensorData<int8_t>(input), op_data->input_zp,
|
||||
op_data->input_scale, input->dims->data, input->dims->size,
|
||||
tflite::micro::GetTensorData<int8_t>(output), op_data->output_zp,
|
||||
op_data->output_scale, output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_index, resolved_axis, temp_buffer,
|
||||
/*compute_sum=*/true));
|
||||
} break;
|
||||
case kTfLiteInt16: {
|
||||
int32_t* temp_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, op_data->temp_buffer_idx));
|
||||
TF_LITE_ENSURE(
|
||||
context,
|
||||
reference_ops::QuantizedMeanOrSum(
|
||||
tflite::micro::GetTensorData<int16_t>(input), op_data->input_zp,
|
||||
op_data->input_scale, input->dims->data, input->dims->size,
|
||||
tflite::micro::GetTensorData<int16_t>(output), op_data->output_zp,
|
||||
op_data->output_scale, output->dims->data, output->dims->size,
|
||||
tflite::micro::GetTensorData<int>(axis), num_axis,
|
||||
params->keep_dims, temp_index, resolved_axis, temp_buffer,
|
||||
/*compute_sum=*/true));
|
||||
} break;
|
||||
default:
|
||||
MicroPrintf("Only float32, int8, and int16 types are supported.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,115 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/resize_bilinear.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kSizeTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TfLiteTensor* size =
|
||||
micro_context->AllocateTempInputTensor(node, kSizeTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(size), 1);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, size->type, kTfLiteInt32);
|
||||
output->type = input->type;
|
||||
|
||||
TF_LITE_ENSURE_MSG(context, IsConstantTensor(size),
|
||||
"Non constant size tensor not supported");
|
||||
|
||||
// Ensure params are valid.
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteResizeBilinearParams*>(node->builtin_data);
|
||||
if (params->half_pixel_centers && params->align_corners) {
|
||||
MicroPrintf("If half_pixel_centers is True, align_corners must be False.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(size);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteResizeBilinearParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* size =
|
||||
tflite::micro::GetEvalInput(context, node, kSizeTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
tflite::ResizeBilinearParams op_params;
|
||||
op_params.align_corners = params->align_corners;
|
||||
op_params.half_pixel_centers = params->half_pixel_centers;
|
||||
reference_ops::ResizeBilinear(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(size),
|
||||
tflite::micro::GetTensorData<int32_t>(size),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
tflite::ResizeBilinearParams op_params;
|
||||
op_params.align_corners = params->align_corners;
|
||||
op_params.half_pixel_centers = params->half_pixel_centers;
|
||||
reference_ops::ResizeBilinearInteger(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(size),
|
||||
tflite::micro::GetTensorData<int32_t>(size),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else {
|
||||
MicroPrintf("Output type is %d, requires float or int8.", output->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_RESIZE_BILINEAR() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,125 +0,0 @@
|
||||
/* Copyright 2017 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/kernels/internal/reference/resize_nearest_neighbor.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace resize_nearest_neighbor {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kSizeTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TfLiteTensor* size =
|
||||
micro_context->AllocateTempInputTensor(node, kSizeTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
// Our current implementations rely on the input being 4D,
|
||||
// and the size being 1D tensor with exactly 2 elements.
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(size), 1);
|
||||
TF_LITE_ENSURE_EQ(context, size->type, kTfLiteInt32);
|
||||
TF_LITE_ENSURE_EQ(context, size->dims->data[0], 2);
|
||||
|
||||
output->type = input->type;
|
||||
|
||||
if (!IsConstantTensor(size)) {
|
||||
MicroPrintf("Dynamic tensors are unsupported in tfmicro.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(size);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteResizeNearestNeighborParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* size =
|
||||
tflite::micro::GetEvalInput(context, node, kSizeTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
tflite::ResizeNearestNeighborParams op_params;
|
||||
op_params.align_corners = params->align_corners;
|
||||
op_params.half_pixel_centers = false;
|
||||
|
||||
if (output->type == kTfLiteFloat32) {
|
||||
reference_ops::ResizeNearestNeighbor(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int32_t>(input),
|
||||
tflite::micro::GetTensorShape(size),
|
||||
tflite::micro::GetTensorData<int32_t>(size),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int32_t>(output));
|
||||
} else if (output->type == kTfLiteInt8) {
|
||||
reference_ops::ResizeNearestNeighbor(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(size),
|
||||
tflite::micro::GetTensorData<int32_t>(size),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
} else if (output->type == kTfLiteInt16) {
|
||||
reference_ops::ResizeNearestNeighbor(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(size),
|
||||
tflite::micro::GetTensorData<int32_t>(size),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
} else {
|
||||
MicroPrintf("Output tensor type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(output->type), output->type);
|
||||
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace resize_nearest_neighbor
|
||||
|
||||
TfLiteRegistration Register_RESIZE_NEAREST_NEIGHBOR() {
|
||||
return tflite::micro::RegisterOp(nullptr, resize_nearest_neighbor::Prepare,
|
||||
resize_nearest_neighbor::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,196 +0,0 @@
|
||||
/* Copyright 2022 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/kernels/internal/reference/select.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
constexpr int kInputTensorCondition = 0;
|
||||
constexpr int kInputTensorX = 1;
|
||||
constexpr int kInputTensorY = 2;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpData {
|
||||
bool requires_broadcast;
|
||||
// True if input condition is scalar or input condition has rank one and
|
||||
// matches the first dimension of other inputs.
|
||||
bool has_low_rank_input_condition;
|
||||
};
|
||||
|
||||
void* SelectInit(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
auto* data = static_cast<OpData*>(
|
||||
context->AllocatePersistentBuffer(context, sizeof(OpData)));
|
||||
data->requires_broadcast = false;
|
||||
data->has_low_rank_input_condition = false;
|
||||
return data;
|
||||
}
|
||||
|
||||
TfLiteStatus CheckBroadcastShape(TfLiteContext* context,
|
||||
const TfLiteTensor* input1,
|
||||
const TfLiteTensor* input2,
|
||||
const TfLiteTensor* input3,
|
||||
const TfLiteIntArray* output_shape) {
|
||||
const int dims1 = NumDimensions(input1);
|
||||
const int dims2 = NumDimensions(input2);
|
||||
const int dims3 = NumDimensions(input3);
|
||||
const int out_dims = std::max(std::max(dims1, dims2), dims3);
|
||||
TF_LITE_ENSURE_EQ(context, out_dims, output_shape->size);
|
||||
|
||||
for (int i = 0; i < out_dims; ++i) {
|
||||
const int d1 = i >= dims1 ? 1 : SizeOfDimension(input1, dims1 - i - 1);
|
||||
const int d2 = i >= dims2 ? 1 : SizeOfDimension(input2, dims2 - i - 1);
|
||||
const int d3 = i >= dims3 ? 1 : SizeOfDimension(input3, dims3 - i - 1);
|
||||
const int min_value = std::min(std::min(d1, d2), d3);
|
||||
int max_value = std::max(std::max(d1, d2), d3);
|
||||
// If one dimention is 0, others must be 0 or 1.
|
||||
if (min_value == 0) max_value = 0;
|
||||
if (!(d1 == 1 || d1 == max_value) || !(d2 == 1 || d2 == max_value) ||
|
||||
!(d3 == 1 || d3 == max_value)) {
|
||||
MicroPrintf("Given shapes are not broadcastable.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_EQ(context, output_shape->data[out_dims - i - 1], max_value);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus SelectPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpData* data = reinterpret_cast<OpData*>(node->user_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input_condition =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
|
||||
|
||||
TfLiteTensor* input_x =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorX);
|
||||
|
||||
TfLiteTensor* input_y =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorY);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
// Input must be bool.
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input_condition->type, kTfLiteBool);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input_x->type, input_y->type);
|
||||
output->type = input_x->type;
|
||||
|
||||
// Respect the original output shape when there are mixed shapes to represent
|
||||
// a scalar data.
|
||||
if (GetTensorShape(input_condition).FlatSize() == 1 &&
|
||||
GetTensorShape(input_x).FlatSize() == 1 &&
|
||||
GetTensorShape(input_y).FlatSize() == 1 &&
|
||||
GetTensorShape(output).FlatSize() == 1) {
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
bool same_shape = HaveSameShapes(input_condition, input_x) &&
|
||||
HaveSameShapes(input_x, input_y);
|
||||
if (!same_shape) {
|
||||
TF_LITE_ENSURE_OK(
|
||||
context, CheckBroadcastShape(context, input_condition, input_x, input_y,
|
||||
output->dims));
|
||||
data->requires_broadcast = true;
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input_condition);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_x);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_y);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus SelectEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input_condition =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
|
||||
|
||||
TfLiteTensor* input_x =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorX);
|
||||
|
||||
TfLiteTensor* input_y =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensorY);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
|
||||
#define TF_LITE_SELECT(type, op) \
|
||||
reference_ops::op(GetTensorShape(input_condition), \
|
||||
GetTensorData<bool>(input_condition), \
|
||||
GetTensorShape(input_x), GetTensorData<type>(input_x), \
|
||||
GetTensorShape(input_y), GetTensorData<type>(input_y), \
|
||||
GetTensorShape(output), GetTensorData<type>(output));
|
||||
|
||||
#define TF_LITE_SWITCH(type, op) \
|
||||
switch (type) { \
|
||||
case kTfLiteFloat32: \
|
||||
TF_LITE_SELECT(float, op); \
|
||||
break; \
|
||||
case kTfLiteInt8: \
|
||||
TF_LITE_SELECT(int8_t, op); \
|
||||
break; \
|
||||
case kTfLiteInt16: \
|
||||
TF_LITE_SELECT(int16_t, op); \
|
||||
break; \
|
||||
default: \
|
||||
MicroPrintf("Does not support type other than %s, but got %s", \
|
||||
"int8|int16|float32", TfLiteTypeGetName(type)); \
|
||||
return kTfLiteError; \
|
||||
}
|
||||
|
||||
if (data->has_low_rank_input_condition) {
|
||||
MicroPrintf("Not yet implemented.");
|
||||
return kTfLiteError;
|
||||
} else if (data->requires_broadcast) {
|
||||
TF_LITE_SWITCH(input_x->type, BroadcastSelect5DSlow);
|
||||
} else {
|
||||
TF_LITE_SWITCH(input_x->type, Select);
|
||||
}
|
||||
|
||||
#undef TF_LITE_SELECT
|
||||
#undef TF_LITE_SWITCH
|
||||
micro_context->DeallocateTempTfLiteTensor(input_condition);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_x);
|
||||
micro_context->DeallocateTempTfLiteTensor(input_y);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// SelectV2 op selects values of 'x' if the corresponding value of 'condition'
|
||||
// is true or the value of 'y' if false. There are valid condition input sizes:
|
||||
//
|
||||
// 1. Either the same shape (in which case the select is elementwise), or
|
||||
// 2. Broadcastable shapes between 'condition', 'x' and 'y'.
|
||||
TfLiteRegistration Register_SELECT_V2() {
|
||||
return tflite::micro::RegisterOp(tflite::SelectInit, tflite::SelectPrepare,
|
||||
tflite::SelectEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,66 +0,0 @@
|
||||
/* Copyright 2017 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
void ExtractShape(const TfLiteEvalTensor* input, int32_t* output_data) {
|
||||
for (int i = 0; i < input->dims->size; ++i) {
|
||||
output_data[i] = input->dims->data[i];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
if (output->type != kTfLiteInt32) {
|
||||
MicroPrintf("Output type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(output->type), output->type);
|
||||
return kTfLiteError;
|
||||
} else {
|
||||
ExtractShape(input, tflite::micro::GetTensorData<int32_t>(output));
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SHAPE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,157 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/slice.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kBeginTensor = 1;
|
||||
constexpr int kSizeTensor = 2;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
const int kMaxDim = 5;
|
||||
|
||||
template <typename T>
|
||||
void GetBeginAndSizeVectors(int dimensions, const TfLiteEvalTensor* begin,
|
||||
const TfLiteEvalTensor* size, int32_t* begins,
|
||||
int32_t* sizes) {
|
||||
int offset = kMaxDim - dimensions;
|
||||
for (int idx = 0; idx < dimensions; ++idx) {
|
||||
begins[offset + idx] = tflite::micro::GetTensorData<T>(begin)[idx];
|
||||
sizes[offset + idx] = tflite::micro::GetTensorData<T>(size)[idx];
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TFLITE_DCHECK(input != nullptr);
|
||||
TfLiteTensor* begin =
|
||||
micro_context->AllocateTempInputTensor(node, kBeginTensor);
|
||||
TFLITE_DCHECK(begin != nullptr);
|
||||
TfLiteTensor* size =
|
||||
micro_context->AllocateTempInputTensor(node, kSizeTensor);
|
||||
TFLITE_DCHECK(size != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TFLITE_DCHECK(output != nullptr);
|
||||
|
||||
// Ensure validity of input tensor and its dimension.
|
||||
TFLITE_DCHECK(input->type == output->type);
|
||||
TFLITE_DCHECK(begin->type == size->type);
|
||||
TFLITE_DCHECK(begin->type == kTfLiteInt32 || begin->type == kTfLiteInt64);
|
||||
TFLITE_DCHECK(size->type == kTfLiteInt32 || size->type == kTfLiteInt64);
|
||||
TFLITE_DCHECK(NumDimensions(begin) == 1);
|
||||
TFLITE_DCHECK(NumDimensions(size) == 1);
|
||||
TFLITE_DCHECK(NumElements(begin) == NumElements(size));
|
||||
TFLITE_DCHECK(NumDimensions(input) <= kMaxDim);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(begin);
|
||||
micro_context->DeallocateTempTfLiteTensor(size);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* begin =
|
||||
tflite::micro::GetEvalInput(context, node, kBeginTensor);
|
||||
const TfLiteEvalTensor* size =
|
||||
tflite::micro::GetEvalInput(context, node, kSizeTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
tflite::SliceParams op_params;
|
||||
op_params.begin_count = kMaxDim;
|
||||
op_params.size_count = kMaxDim;
|
||||
for (int i = 0; i < kMaxDim; ++i) {
|
||||
op_params.begin[i] = 0;
|
||||
op_params.size[i] = 1;
|
||||
}
|
||||
|
||||
if (begin->type == kTfLiteInt32) {
|
||||
GetBeginAndSizeVectors<int32_t>(input->dims->size, begin, size,
|
||||
op_params.begin, op_params.size);
|
||||
} else if (begin->type == kTfLiteInt64) {
|
||||
GetBeginAndSizeVectors<int64_t>(input->dims->size, begin, size,
|
||||
op_params.begin, op_params.size);
|
||||
} else {
|
||||
MicroPrintf("Begin tensor type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::Slice<float>(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
reference_ops::Slice<int32_t>(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int32_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int32_t>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::Slice<int8_t>(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
case kTfLiteInt16:
|
||||
reference_ops::Slice<int16_t>(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Input tensor type %s (%d) not supported.",
|
||||
TfLiteTypeGetName(input->type), input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SLICE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,89 +0,0 @@
|
||||
/* Copyright 2021 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/micro/kernels/softmax.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/softmax.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
void SoftmaxQuantized(const TfLiteEvalTensor* input, TfLiteEvalTensor* output,
|
||||
const SoftmaxParams& op_data) {
|
||||
if (input->type == kTfLiteInt8) {
|
||||
if (output->type == kTfLiteInt16) {
|
||||
tflite::reference_ops::Softmax(
|
||||
op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
} else {
|
||||
tflite::reference_ops::Softmax(
|
||||
op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
}
|
||||
} else {
|
||||
tflite::reference_ops::SoftmaxInt16(
|
||||
op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
SoftmaxParams op_data = *static_cast<SoftmaxParams*>(node->user_data);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
tflite::reference_ops::Softmax(
|
||||
op_data, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
case kTfLiteInt8:
|
||||
case kTfLiteInt16: {
|
||||
SoftmaxQuantized(input, output, op_data);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SOFTMAX() {
|
||||
return tflite::micro::RegisterOp(SoftmaxInit, SoftmaxPrepare, SoftmaxEval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,120 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/space_to_batch_nd.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kBlockShapeTensor = 1;
|
||||
constexpr int kCropsTensor = 2;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// Currently, only 3D NHC and 4D NHWC input/output op_context are supported.
|
||||
// In case of 3D input, it will be extended to 3D NHWC by adding W=1.
|
||||
// The 4D array need to have exactly 2 spatial dimensions.
|
||||
// TODO(b/149952582): Support arbitrary dimension in SpaceToBatchND.
|
||||
const int kInputOutputMinDimensionNum = 3;
|
||||
const int kInputOutputMaxDimensionNum = 4;
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(SpaceToBatchParams));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr && output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) >= kInputOutputMinDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(output) >= kInputOutputMinDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(input) <= kInputOutputMaxDimensionNum);
|
||||
TF_LITE_ENSURE(context, NumDimensions(output) <= kInputOutputMaxDimensionNum);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const SpaceToBatchParams& params =
|
||||
*(static_cast<const SpaceToBatchParams*>(node->user_data));
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* block_shape =
|
||||
tflite::micro::GetEvalInput(context, node, kBlockShapeTensor);
|
||||
const TfLiteEvalTensor* crops =
|
||||
tflite::micro::GetEvalInput(context, node, kCropsTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::SpaceToBatchND(
|
||||
params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(block_shape),
|
||||
tflite::micro::GetTensorData<int32_t>(block_shape),
|
||||
tflite::micro::GetTensorShape(crops),
|
||||
tflite::micro::GetTensorData<int32_t>(crops),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::SpaceToBatchND(
|
||||
params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(block_shape),
|
||||
tflite::micro::GetTensorData<int32_t>(block_shape),
|
||||
tflite::micro::GetTensorShape(crops),
|
||||
tflite::micro::GetTensorData<int32_t>(crops),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace.
|
||||
|
||||
TfLiteRegistration Register_SPACE_TO_BATCH_ND() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,126 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/space_to_depth.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
constexpr int kBatchRank = 0;
|
||||
constexpr int kHeightRank = 1;
|
||||
constexpr int kWidthRank = 2;
|
||||
constexpr int kDepthRank = 3;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteSpaceToDepthParams*>(node->builtin_data);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
|
||||
|
||||
auto data_type = output->type;
|
||||
TF_LITE_ENSURE(context,
|
||||
data_type == kTfLiteFloat32 || data_type == kTfLiteInt8);
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
const int block_size = params->block_size;
|
||||
const int input_height = input->dims->data[kHeightRank];
|
||||
const int input_width = input->dims->data[kWidthRank];
|
||||
int output_height = input_height / block_size;
|
||||
int output_width = input_width / block_size;
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input_height, output_height * block_size);
|
||||
TF_LITE_ENSURE_EQ(context, input_width, output_width * block_size);
|
||||
|
||||
// Relocate dims to the persistent storage arena before changing them,
|
||||
// otherwise we'd be modifying temporary copies made by the interpreters each
|
||||
// time they process the layer.
|
||||
TfLiteEvalTensor* output_eval =
|
||||
micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
TF_LITE_ENSURE_OK(context, micro::CreateWritableTensorDimsWithCopy(
|
||||
context, output, output_eval));
|
||||
|
||||
output->dims->data[kBatchRank] = input->dims->data[kBatchRank];
|
||||
output->dims->data[kHeightRank] = output_height;
|
||||
output->dims->data[kWidthRank] = output_width;
|
||||
output->dims->data[kDepthRank] =
|
||||
input->dims->data[kDepthRank] * block_size * block_size;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
auto* params =
|
||||
reinterpret_cast<TfLiteSpaceToDepthParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output = micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
SpaceToDepthParams op_params;
|
||||
op_params.block_size = params->block_size;
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::SpaceToDepth(op_params, micro::GetTensorShape(input),
|
||||
micro::GetTensorData<float>(input),
|
||||
micro::GetTensorShape(output),
|
||||
micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::SpaceToDepth(op_params, micro::GetTensorShape(input),
|
||||
micro::GetTensorData<int8_t>(input),
|
||||
micro::GetTensorShape(output),
|
||||
micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("SPACE_TO_DEPTH only supports FLOAT32 and INT8, got %s.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SPACE_TO_DEPTH() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,129 +0,0 @@
|
||||
/* Copyright 2018 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace split_v {
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus SplitImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input, int axis_value) {
|
||||
const TfLiteIntArray* input_dims = input->dims;
|
||||
const TfLiteEvalTensor* output0 =
|
||||
tflite::micro::GetEvalOutput(context, node, 0);
|
||||
|
||||
const int split_dimensions = input_dims->size;
|
||||
|
||||
TFLITE_DCHECK_LT(axis_value, split_dimensions);
|
||||
TFLITE_DCHECK_EQ(output0->dims->size, split_dimensions);
|
||||
|
||||
int64_t split_size = 0;
|
||||
const int output_count = NumOutputs(node);
|
||||
for (int i = 0; i < output_count; i++) {
|
||||
split_size +=
|
||||
tflite::micro::GetEvalOutput(context, node, i)->dims->data[axis_value];
|
||||
}
|
||||
TFLITE_DCHECK_EQ(split_size, input_dims->data[axis_value]);
|
||||
int64_t outer_size = 1;
|
||||
for (int i = 0; i < axis_value; ++i) {
|
||||
outer_size *= input_dims->data[i];
|
||||
}
|
||||
|
||||
int64_t base_inner_size = 1;
|
||||
for (int i = axis_value + 1; i < split_dimensions; ++i) {
|
||||
base_inner_size *= input_dims->data[i];
|
||||
}
|
||||
|
||||
const T* input_ptr = tflite::micro::GetTensorData<T>(input);
|
||||
for (int k = 0; k < outer_size; ++k) {
|
||||
for (int i = 0; i < output_count; ++i) {
|
||||
TfLiteEvalTensor* output_tensor =
|
||||
tflite::micro::GetEvalOutput(context, node, i);
|
||||
T* output_data = tflite::micro::GetTensorData<T>(output_tensor);
|
||||
const int copy_size =
|
||||
output_tensor->dims->data[axis_value] * base_inner_size;
|
||||
T* output_ptr = output_data + k * copy_size;
|
||||
for (int j = 0; j < copy_size; ++j) output_ptr[j] = input_ptr[j];
|
||||
input_ptr += copy_size;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
// Dynamic output tensors are needed if axis tensor is not constant.
|
||||
// But Micro doesn't support dynamic memory allocation, so we only support
|
||||
// constant axis tensor for now.
|
||||
TfLiteTensor* axis = micro_context->AllocateTempInputTensor(node, 2);
|
||||
TF_LITE_ENSURE_MSG(context, IsConstantTensor(axis),
|
||||
"Non constant axis tensor not supported");
|
||||
micro_context->DeallocateTempTfLiteTensor(axis);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
const TfLiteEvalTensor* axis = tflite::micro::GetEvalInput(context, node, 2);
|
||||
|
||||
int axis_value = tflite::micro::GetTensorData<int32_t>(axis)[0];
|
||||
if (axis_value < 0) {
|
||||
axis_value += input->dims->size;
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE(context, axis_value >= 0);
|
||||
TF_LITE_ENSURE(context, axis_value < input->dims->size);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
return SplitImpl<float>(context, node, input, axis_value);
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
return SplitImpl<int8_t>(context, node, input, axis_value);
|
||||
}
|
||||
case kTfLiteInt16: {
|
||||
return SplitImpl<int16_t>(context, node, input, axis_value);
|
||||
}
|
||||
case kTfLiteInt32: {
|
||||
return SplitImpl<int32_t>(context, node, input, axis_value);
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Type %s currently not supported.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace split_v
|
||||
|
||||
TfLiteRegistration Register_SPLIT_V() {
|
||||
return tflite::micro::RegisterOp(nullptr, split_v::Prepare, split_v::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,117 +0,0 @@
|
||||
/* Copyright 2020 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/process_broadcast_shapes.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
struct SqueezeContext {
|
||||
SqueezeContext(TfLiteContext* context, TfLiteNode* node) {
|
||||
params = reinterpret_cast<TfLiteSqueezeParams*>(node->builtin_data);
|
||||
micro_context = GetMicroContext(context);
|
||||
input = micro_context->AllocateTempInputTensor(node, 0);
|
||||
output = micro_context->AllocateTempOutputTensor(node, 0);
|
||||
}
|
||||
~SqueezeContext() {
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
}
|
||||
MicroContext* micro_context;
|
||||
TfLiteSqueezeParams* params;
|
||||
TfLiteTensor* input;
|
||||
TfLiteTensor* output;
|
||||
};
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
SqueezeContext op_context(context, node);
|
||||
const int input_num_dims = NumDimensions(op_context.input);
|
||||
const int num_squeeze_dims = op_context.params->num_squeeze_dims;
|
||||
|
||||
// Determines number of dimensions of output tensor after squeeze.
|
||||
const TfLiteIntArray* input_dims = op_context.input->dims;
|
||||
const TfLiteIntArray* output_dims = op_context.output->dims;
|
||||
const int* squeeze_dims = op_context.params->squeeze_dims;
|
||||
|
||||
constexpr int max_squeeze_dims = 8;
|
||||
TF_LITE_ENSURE(context, input_num_dims <= max_squeeze_dims);
|
||||
bool should_squeeze[max_squeeze_dims] = {};
|
||||
|
||||
if (num_squeeze_dims == 0) {
|
||||
for (int idx = 0; idx < input_num_dims; ++idx) {
|
||||
if (input_dims->data[idx] == 1) {
|
||||
should_squeeze[idx] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int idx = 0; idx < num_squeeze_dims; ++idx) {
|
||||
int current = squeeze_dims[idx] < 0 ? squeeze_dims[idx] + input_num_dims
|
||||
: squeeze_dims[idx];
|
||||
TF_LITE_ENSURE(context, current >= 0 && current < input_num_dims &&
|
||||
input_dims->data[current] == 1);
|
||||
should_squeeze[current] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure output dimensions are big enough.
|
||||
for (int in_idx = 0, out_idx = 0; in_idx < input_num_dims; ++in_idx) {
|
||||
if (!should_squeeze[in_idx]) {
|
||||
TFLITE_CHECK_GE(output_dims->data[out_idx++], input_dims->data[in_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
|
||||
|
||||
if (input->type == kTfLiteString) {
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);
|
||||
size_t input_byte_size;
|
||||
size_t output_byte_size;
|
||||
TF_LITE_ENSURE_OK(context,
|
||||
TfLiteEvalTensorByteLength(input, &input_byte_size));
|
||||
TF_LITE_ENSURE_OK(context,
|
||||
TfLiteEvalTensorByteLength(output, &output_byte_size));
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input_byte_size, output_byte_size);
|
||||
memcpy(output->data.raw, input->data.raw, input_byte_size);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_SQUEEZE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,209 +0,0 @@
|
||||
/* Copyright 2018 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/kernels/internal/reference/strided_slice.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace strided_slice {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kBeginTensor = 1;
|
||||
constexpr int kEndTensor = 2;
|
||||
constexpr int kStridesTensor = 3;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct StridedSliceContext {
|
||||
StridedSliceContext(TfLiteContext* context, TfLiteNode* node) {
|
||||
params = reinterpret_cast<TfLiteStridedSliceParams*>(node->builtin_data);
|
||||
micro_context = GetMicroContext(context);
|
||||
input = micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
begin = micro_context->AllocateTempInputTensor(node, kBeginTensor);
|
||||
end = micro_context->AllocateTempInputTensor(node, kEndTensor);
|
||||
strides = micro_context->AllocateTempInputTensor(node, kStridesTensor);
|
||||
output = micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
dims = NumDimensions(input);
|
||||
}
|
||||
~StridedSliceContext() {
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(begin);
|
||||
micro_context->DeallocateTempTfLiteTensor(end);
|
||||
micro_context->DeallocateTempTfLiteTensor(strides);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
}
|
||||
const TfLiteStridedSliceParams* params;
|
||||
MicroContext* micro_context;
|
||||
TfLiteTensor* input;
|
||||
TfLiteTensor* begin;
|
||||
TfLiteTensor* end;
|
||||
TfLiteTensor* strides;
|
||||
TfLiteTensor* output;
|
||||
int dims;
|
||||
};
|
||||
|
||||
// This Op only supports 1-4D cases and since we use the reference 4D
|
||||
// implementation, the 1-3D tensors are mapped to 4D.
|
||||
const int kMaxDim = 4;
|
||||
|
||||
tflite::StridedSliceParams BuildStridedSliceParams(
|
||||
StridedSliceContext* op_context) {
|
||||
tflite::StridedSliceParams op_params;
|
||||
op_params.start_indices_count = op_context->dims;
|
||||
op_params.stop_indices_count = op_context->dims;
|
||||
op_params.strides_count = op_context->dims;
|
||||
|
||||
for (int i = 0; i < op_context->dims; ++i) {
|
||||
op_params.start_indices[i] = GetTensorData<int32_t>(op_context->begin)[i];
|
||||
op_params.stop_indices[i] = GetTensorData<int32_t>(op_context->end)[i];
|
||||
op_params.strides[i] = GetTensorData<int32_t>(op_context->strides)[i];
|
||||
}
|
||||
|
||||
op_params.begin_mask = op_context->params->begin_mask;
|
||||
op_params.ellipsis_mask = 0;
|
||||
op_params.end_mask = op_context->params->end_mask;
|
||||
op_params.new_axis_mask = 0;
|
||||
op_params.shrink_axis_mask = op_context->params->shrink_axis_mask;
|
||||
return op_params;
|
||||
}
|
||||
|
||||
// Processes the indexing tensors (begin, end and strides) to resize the
|
||||
// output tensor. This function is callable from both Prepare() and Eval() as
|
||||
// long as the caller ensures the indexing tensors are present.
|
||||
TfLiteStatus CheckOutputSize(TfLiteContext* context,
|
||||
StridedSliceContext* op_context) {
|
||||
using ::tflite::strided_slice::StartForAxis;
|
||||
using ::tflite::strided_slice::StopForAxis;
|
||||
TfLiteIntArray* output_shape = op_context->output->dims;
|
||||
int shape_size = 0;
|
||||
auto op_params = BuildStridedSliceParams(op_context);
|
||||
auto input_shape = GetTensorShape(op_context->input);
|
||||
for (int idx = 0; idx < op_context->dims; ++idx) {
|
||||
int32_t stride = GetTensorData<int32_t>(op_context->strides)[idx];
|
||||
TF_LITE_ENSURE_MSG(context, stride != 0, "stride value has to be non-zero");
|
||||
int32_t begin = StartForAxis(op_params, input_shape, idx);
|
||||
int32_t end = StopForAxis(op_params, input_shape, idx, begin);
|
||||
|
||||
// When shrinking an axis, the end position does not matter (and can be
|
||||
// incorrect when negative indexing is used, see Issue #19260). Always use
|
||||
// begin + 1 to generate a length 1 slice, since begin has
|
||||
// already been adjusted for negative indices by StartForAxis.
|
||||
const bool shrink_axis = op_context->params->shrink_axis_mask & (1 << idx);
|
||||
if (shrink_axis) {
|
||||
end = begin + 1;
|
||||
}
|
||||
|
||||
// This is valid for both positive and negative strides
|
||||
int32_t dim_shape = std::ceil((end - begin) / static_cast<float>(stride));
|
||||
dim_shape = dim_shape < 0 ? 0 : dim_shape;
|
||||
if (!shrink_axis) {
|
||||
TF_LITE_ENSURE_EQ(context, output_shape->data[shape_size], dim_shape);
|
||||
shape_size++;
|
||||
}
|
||||
}
|
||||
TF_LITE_ENSURE_EQ(context, output_shape->size, shape_size);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(StridedSliceParams));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
StridedSliceParams* op_params =
|
||||
static_cast<StridedSliceParams*>(node->user_data);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 4);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
StridedSliceContext op_context(context, node);
|
||||
TF_LITE_ENSURE_MSG(context, op_context.dims <= kMaxDim,
|
||||
"input dim should not exceed 4");
|
||||
auto params = BuildStridedSliceParams(&op_context);
|
||||
memcpy(op_params, ¶ms, sizeof(StridedSliceParams));
|
||||
return CheckOutputSize(context, &op_context);
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const StridedSliceParams& op_params =
|
||||
*(static_cast<const StridedSliceParams*>(node->user_data));
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
switch (output->type) {
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::StridedSlice(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::StridedSlice(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
case kTfLiteInt16:
|
||||
reference_ops::StridedSlice(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
reference_ops::StridedSlice(
|
||||
op_params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int32_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int32_t>(output));
|
||||
break;
|
||||
case kTfLiteBool:
|
||||
reference_ops::StridedSlice(op_params,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<bool>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<bool>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace strided_slice
|
||||
|
||||
TfLiteRegistration Register_STRIDED_SLICE() {
|
||||
return tflite::micro::RegisterOp(strided_slice::Init, strided_slice::Prepare,
|
||||
strided_slice::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,99 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_SVDF_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_SVDF_H_
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
struct OpDataSvdf {
|
||||
int32_t effective_scale_1_a;
|
||||
int32_t effective_scale_2_a;
|
||||
// b versions of each scale are kept at int since the numbers are just the
|
||||
// shift value - typically between [-32, 32].
|
||||
int effective_scale_1_b;
|
||||
int effective_scale_2_b;
|
||||
int scratch_tensor_index;
|
||||
int scratch_output_tensor_index;
|
||||
|
||||
// Cached tensor zero point values for quantized operations.
|
||||
int input_zero_point;
|
||||
int output_zero_point;
|
||||
int activation_state_zero_point;
|
||||
};
|
||||
|
||||
// Input tensors.
|
||||
extern const int kSvdfInputTensor;
|
||||
extern const int kSvdfWeightsFeatureTensor;
|
||||
extern const int kSvdfWeightsTimeTensor;
|
||||
extern const int kSvdfBiasTensor;
|
||||
// This is a variable tensor, and will be modified by this op.
|
||||
extern const int kSvdfInputActivationStateTensor;
|
||||
|
||||
// Output tensor.
|
||||
extern const int kSvdfOutputTensor;
|
||||
|
||||
void EvalInt8SvdfReference(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input_tensor,
|
||||
const TfLiteEvalTensor* weights_feature_tensor,
|
||||
const TfLiteEvalTensor* weights_time_tensor,
|
||||
const TfLiteEvalTensor* bias_tensor,
|
||||
const TfLiteSVDFParams* params,
|
||||
TfLiteEvalTensor* activation_state_tensor,
|
||||
TfLiteEvalTensor* output_tensor,
|
||||
const OpDataSvdf& data);
|
||||
|
||||
// TODO(#523): remove 16-bit code when no longer needed.
|
||||
void EvalInt16SvdfReference(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input_tensor,
|
||||
const TfLiteEvalTensor* weights_feature_tensor,
|
||||
const TfLiteEvalTensor* weights_time_tensor,
|
||||
const TfLiteEvalTensor* bias_tensor,
|
||||
const TfLiteSVDFParams* params,
|
||||
TfLiteEvalTensor* activation_state_tensor,
|
||||
TfLiteEvalTensor* output_tensor,
|
||||
const OpDataSvdf& data);
|
||||
|
||||
void EvalFloatSvdfReference(
|
||||
TfLiteContext* context, TfLiteNode* node, const TfLiteEvalTensor* input,
|
||||
const TfLiteEvalTensor* weights_feature,
|
||||
const TfLiteEvalTensor* weights_time, const TfLiteEvalTensor* bias,
|
||||
const TfLiteSVDFParams* params, int scratch_tensor_index,
|
||||
TfLiteEvalTensor* activation_state, TfLiteEvalTensor* output);
|
||||
|
||||
TfLiteStatus PrepareSvdf(TfLiteContext* context, TfLiteNode* node);
|
||||
|
||||
// This is the most generic TfLiteRegistration. The actual supported types may
|
||||
// still be target dependent. The only requirement is that every implementation
|
||||
// (reference or optimized) must define this function.
|
||||
TfLiteRegistration Register_SVDF();
|
||||
|
||||
#if defined(HEXAGON) || defined(CMSIS_NN)
|
||||
TfLiteRegistration Register_SVDF_INT8();
|
||||
|
||||
#else
|
||||
// Note that while this block gets used for both reference and optimized kernels
|
||||
// that do not have any specialized implementations, the only goal here is to
|
||||
// define fallback implementation that allow reference kernels to still be used
|
||||
// from applications that call a more specific kernel variant.
|
||||
|
||||
inline TfLiteRegistration Register_SVDF_INT8() { return Register_SVDF(); }
|
||||
|
||||
#endif
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_SVDF_H_
|
||||
@@ -1,203 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/integer_ops/tanh.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/tanh.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/micro_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace activations {
|
||||
namespace {
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct OpData {
|
||||
int32_t input_zero_point;
|
||||
int32_t input_range_radius;
|
||||
int32_t input_multiplier;
|
||||
int input_left_shift;
|
||||
};
|
||||
|
||||
void* TanhInit(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus CalculateArithmeticOpData(TfLiteContext* context, TfLiteNode* node,
|
||||
OpData* data) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
|
||||
|
||||
if (input->type == kTfLiteInt8) {
|
||||
static constexpr int kInputIntegerBits = 4;
|
||||
const double input_real_multiplier =
|
||||
static_cast<double>(input->params.scale) *
|
||||
static_cast<double>(1 << (31 - kInputIntegerBits));
|
||||
|
||||
const double q = std::frexp(input_real_multiplier, &data->input_left_shift);
|
||||
data->input_multiplier = static_cast<int32_t>(TfLiteRound(q * (1ll << 31)));
|
||||
|
||||
data->input_range_radius =
|
||||
CalculateInputRadius(kInputIntegerBits, data->input_left_shift, 31);
|
||||
}
|
||||
|
||||
if (input->type == kTfLiteInt16) {
|
||||
static constexpr int kInputIntegerBits = 3;
|
||||
static constexpr int kOutputFractionalBits = 15;
|
||||
|
||||
// These operators are implemented in fixed-point arithmetic,
|
||||
// which intrinsically wants symmetric ranges (zero_point==0)
|
||||
// and power-of-two scales (power-of-two is abbreviated below as POT).
|
||||
// While more general support would be possible by means of rescaling,
|
||||
// that would add some overhead and some loss of accuracy and wouldn't
|
||||
// be used at the moment as current quantized LSTM applications are
|
||||
// happy with symmetric, power-of-two-scales quantization. So we just
|
||||
// implement that narrow case only for now.
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->params.zero_point, 0);
|
||||
TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
|
||||
|
||||
int input_scale_log2_rounded;
|
||||
bool param_scale_pot =
|
||||
CheckedLog2(input->params.scale, &input_scale_log2_rounded);
|
||||
|
||||
data->input_left_shift =
|
||||
(15 - kInputIntegerBits) + input_scale_log2_rounded;
|
||||
param_scale_pot &=
|
||||
(data->input_left_shift == 0 || data->input_left_shift == 1);
|
||||
|
||||
if (param_scale_pot) {
|
||||
data->input_multiplier = 0;
|
||||
} else {
|
||||
// Calculate multiplier to change input scale to 1/(3*4096)
|
||||
// as required by the table lookup.
|
||||
// The number 3.0 in the multiplier comes from here,
|
||||
// because the interval is [-10.7, 10.7] instead of [-8, 8].
|
||||
// So, in this scaling +/-2^17 represents +/-10.7.
|
||||
|
||||
double multiplier =
|
||||
static_cast<double>(input->params.scale) * 4096.0 * 3.0;
|
||||
data->input_left_shift = 0;
|
||||
|
||||
while (multiplier <= 32767.0 / 2.0 && data->input_left_shift <= 30) {
|
||||
data->input_left_shift++;
|
||||
multiplier = multiplier * 2.0;
|
||||
}
|
||||
|
||||
data->input_multiplier = static_cast<int32_t>(multiplier);
|
||||
}
|
||||
|
||||
int output_scale_log2_rounded;
|
||||
TF_LITE_ENSURE(
|
||||
context, CheckedLog2(output->params.scale, &output_scale_log2_rounded));
|
||||
TF_LITE_ENSURE_EQ(context, output_scale_log2_rounded,
|
||||
-kOutputFractionalBits);
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus TanhPrepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
data->input_zero_point = input->params.zero_point;
|
||||
TF_LITE_ENSURE_OK(context, CalculateArithmeticOpData(context, node, data));
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteStatus TanhEval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData& data = *(static_cast<const OpData*>(node->user_data));
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
reference_ops::Tanh(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
case kTfLiteInt16: {
|
||||
reference_integer_ops::Tanh(
|
||||
data.input_multiplier, data.input_left_shift,
|
||||
tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
case kTfLiteInt8: {
|
||||
reference_integer_ops::Tanh(
|
||||
data.input_zero_point, data.input_range_radius, data.input_multiplier,
|
||||
data.input_left_shift, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
return kTfLiteOk;
|
||||
} break;
|
||||
default:
|
||||
MicroPrintf("Input %s, output %s not supported.",
|
||||
TfLiteTypeGetName(input->type),
|
||||
TfLiteTypeGetName(output->type), context);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace activations
|
||||
|
||||
TfLiteRegistration Register_TANH() {
|
||||
return tflite::micro::RegisterOp(
|
||||
activations::TanhInit, activations::TanhPrepare, activations::TanhEval);
|
||||
}
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,121 +0,0 @@
|
||||
/* Copyright 2020 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/kernels/internal/reference/transpose.h"
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/internal/types.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kPermTensor = 1;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
struct TransposeContext {
|
||||
TransposeContext(TfLiteContext* context, TfLiteNode* node) {
|
||||
micro_context = GetMicroContext(context);
|
||||
input = micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
perm = micro_context->AllocateTempInputTensor(node, kPermTensor);
|
||||
output = micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
}
|
||||
~TransposeContext() {
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(perm);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
}
|
||||
MicroContext* micro_context;
|
||||
TfLiteTensor* input;
|
||||
TfLiteTensor* perm;
|
||||
TfLiteTensor* output;
|
||||
};
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
|
||||
TransposeContext op_context(context, node);
|
||||
|
||||
// Ensure validity of input tensor.
|
||||
TF_LITE_ENSURE_MSG(context, NumDimensions(op_context.input) <= 5,
|
||||
"Transpose op only supports 1D-5D input arrays.");
|
||||
TF_LITE_ENSURE_TYPES_EQ(context, op_context.input->type,
|
||||
op_context.output->type);
|
||||
|
||||
int dims = NumDimensions(op_context.input);
|
||||
const int32_t* perm_data = GetTensorData<int32_t>(op_context.perm);
|
||||
|
||||
// Ensure validity of the permutations tensor as a 1D tensor.
|
||||
TF_LITE_ENSURE_EQ(context, NumDimensions(op_context.perm), 1);
|
||||
TF_LITE_ENSURE_EQ(context, op_context.perm->dims->data[0], dims);
|
||||
for (int idx = 0; idx < dims; ++idx) {
|
||||
TF_LITE_ENSURE_MSG(context, (perm_data[idx] >= 0 && perm_data[idx] < dims),
|
||||
"Transpose op permutations array is out of bounds.");
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* perm_tensor =
|
||||
tflite::micro::GetEvalInput(context, node, kPermTensor);
|
||||
const int32_t* perm_data = perm_tensor->data.i32;
|
||||
const int size = perm_tensor->dims->data[0];
|
||||
TransposeParams params;
|
||||
params.perm_count = size;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
params.perm[i] = perm_data[i];
|
||||
}
|
||||
|
||||
// Transpose kernel only does rearranging values not numeric evaluations
|
||||
// on each cell. It's safe to implement per size of scalar type and this
|
||||
// trick keeps the total code size in a reasonable range.
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32:
|
||||
reference_ops::Transpose(params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output));
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
reference_ops::Transpose(params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output));
|
||||
break;
|
||||
default:
|
||||
MicroPrintf(
|
||||
"Type %s is currently not supported by Transpose. "
|
||||
"Only float32 and int8 is supported",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_TRANSPOSE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
} // namespace tflite
|
||||
@@ -1,343 +0,0 @@
|
||||
/* Copyright 2021 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/kernels/internal/reference/transpose_conv.h"
|
||||
|
||||
#include "tensorflow/lite/c/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/quantization_util.h"
|
||||
#include "tensorflow/lite/kernels/internal/reference/integer_ops/transpose_conv.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/kernels/padding.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
// For the TfLite transpose_conv implementation, input tensor 0 corresponds to
|
||||
// the OutputShapeTensor. However, since TFLM does not support dynamic tensors,
|
||||
// the TFLM implementation ignores input tensor 0 and the only inputs we care
|
||||
// about are kFilterTensor, kInputTensor and kBiasTensor.
|
||||
constexpr int kFilterTensor = 1;
|
||||
constexpr int kInputTensor = 2;
|
||||
constexpr int kBiasTensor = 3;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
// Conv is quantized along dimension 0:
|
||||
// https://www.tensorflow.org/lite/performance/quantization_spec
|
||||
constexpr int kConvQuantizedDimension = 0;
|
||||
|
||||
struct OpData {
|
||||
ConvParams params;
|
||||
|
||||
// A scratch buffer is required for quantized implementations.
|
||||
int scratch_buffer_index;
|
||||
|
||||
// TODO(b/192090531): Remove this once all 8x16 transpose conv models use
|
||||
// 64-bit biases.
|
||||
int bias_converted_buffer_index;
|
||||
|
||||
// Multiplier and shift arrays are required for the int8 implementation.
|
||||
int32_t* per_channel_output_multiplier;
|
||||
int32_t* per_channel_output_shift;
|
||||
};
|
||||
|
||||
inline PaddingType RuntimePaddingType(TfLitePadding padding) {
|
||||
switch (padding) {
|
||||
case TfLitePadding::kTfLitePaddingSame:
|
||||
return PaddingType::kSame;
|
||||
case TfLitePadding::kTfLitePaddingValid:
|
||||
return PaddingType::kValid;
|
||||
case TfLitePadding::kTfLitePaddingUnknown:
|
||||
default:
|
||||
return PaddingType::kNone;
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteTransposeConvParams* params, int width,
|
||||
int height, int filter_width, int filter_height,
|
||||
const TfLiteType data_type, OpData* data) {
|
||||
bool has_bias = node->inputs->size == 4;
|
||||
// Check number of inputs/outputs
|
||||
TF_LITE_ENSURE(context, has_bias || node->inputs->size == 3);
|
||||
TF_LITE_ENSURE_EQ(context, node->outputs->size, 1);
|
||||
|
||||
// Matching GetWindowedOutputSize in TensorFlow.
|
||||
auto padding = params->padding;
|
||||
int unused_output_width;
|
||||
int unused_output_height;
|
||||
TfLitePaddingValues padding_values = ComputePaddingHeightWidth(
|
||||
params->stride_height, params->stride_width, 1,
|
||||
1, // Dilation height and width are always 1 for transpose_conv.
|
||||
height, width, filter_height, filter_width, padding,
|
||||
&unused_output_height, &unused_output_width);
|
||||
|
||||
data->params.padding_type = RuntimePaddingType(padding);
|
||||
data->params.padding_values.width = padding_values.width;
|
||||
data->params.padding_values.height = padding_values.height;
|
||||
|
||||
// Note that quantized inference requires that all tensors have their
|
||||
// parameters set. This is usually done during quantized training.
|
||||
if (data_type != kTfLiteFloat32) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* filter =
|
||||
micro_context->AllocateTempInputTensor(node, kFilterTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
TfLiteTensor* bias =
|
||||
micro_context->AllocateTempInputTensor(node, kBiasTensor);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
int output_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
|
||||
TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams(
|
||||
context, input, filter, bias, output, kTfLiteActNone,
|
||||
&data->params.output_multiplier, &data->params.output_shift,
|
||||
&data->params.quantized_activation_min,
|
||||
&data->params.quantized_activation_max,
|
||||
data->per_channel_output_multiplier, data->per_channel_output_shift,
|
||||
output_channels));
|
||||
|
||||
// TODO(b/192090531): Remove this once all 8x16 transpose conv models use
|
||||
// 64-bit biases.
|
||||
if (input->type == kTfLiteInt16) {
|
||||
TFLITE_DCHECK(filter->type == kTfLiteInt8);
|
||||
TFLITE_DCHECK(output->type == kTfLiteInt16);
|
||||
if (bias->type == kTfLiteInt16) {
|
||||
TFLITE_DCHECK(
|
||||
context->RequestScratchBufferInArena(
|
||||
context, GetTensorShape(bias).FlatSize() * sizeof(std::int64_t),
|
||||
&(data->bias_converted_buffer_index)) == kTfLiteOk);
|
||||
}
|
||||
}
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(filter);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
if (bias != nullptr) {
|
||||
micro_context->DeallocateTempTfLiteTensor(bias);
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* Init(TfLiteContext* context, const char* buffer, size_t length) {
|
||||
TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
|
||||
return context->AllocatePersistentBuffer(context, sizeof(OpData));
|
||||
}
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
TFLITE_DCHECK(node->builtin_data != nullptr);
|
||||
|
||||
OpData* data = static_cast<OpData*>(node->user_data);
|
||||
const auto params =
|
||||
static_cast<const TfLiteTransposeConvParams*>(node->builtin_data);
|
||||
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* filter =
|
||||
micro_context->AllocateTempInputTensor(node, kFilterTensor);
|
||||
TF_LITE_ENSURE(context, filter != nullptr);
|
||||
|
||||
// Get height and width of the output.
|
||||
const int width = SizeOfDimension(output, 2);
|
||||
const int height = SizeOfDimension(output, 1);
|
||||
const int filter_width = SizeOfDimension(filter, 2);
|
||||
const int filter_height = SizeOfDimension(filter, 1);
|
||||
|
||||
// Dynamically allocate per-channel quantization parameters.
|
||||
const int num_channels = filter->dims->data[kConvQuantizedDimension];
|
||||
data->per_channel_output_multiplier =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
data->per_channel_output_shift =
|
||||
static_cast<int32_t*>(context->AllocatePersistentBuffer(
|
||||
context, num_channels * sizeof(int32_t)));
|
||||
|
||||
// Quantized kernels use an int32 scratch buffer.
|
||||
if (input->type == kTfLiteInt8) {
|
||||
TFLITE_DCHECK(context->RequestScratchBufferInArena != nullptr);
|
||||
TFLITE_DCHECK(context->RequestScratchBufferInArena(
|
||||
context,
|
||||
GetTensorShape(output).FlatSize() * sizeof(int32_t),
|
||||
&(data->scratch_buffer_index)) == kTfLiteOk);
|
||||
}
|
||||
|
||||
// Quantized 16x8 kernels use an int64 scratch buffer.
|
||||
if (input->type == kTfLiteInt16) {
|
||||
TFLITE_DCHECK(context->RequestScratchBufferInArena != nullptr);
|
||||
TFLITE_DCHECK(context->RequestScratchBufferInArena(
|
||||
context,
|
||||
GetTensorShape(output).FlatSize() * sizeof(std::int64_t),
|
||||
&(data->scratch_buffer_index)) == kTfLiteOk);
|
||||
}
|
||||
|
||||
// All per-channel quantized tensors need valid zero point and scale arrays.
|
||||
if (input->type == kTfLiteInt8 || input->type == kTfLiteInt16) {
|
||||
TF_LITE_ENSURE_EQ(context, filter->quantization.type,
|
||||
kTfLiteAffineQuantization);
|
||||
|
||||
const auto* affine_quantization =
|
||||
static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
|
||||
TF_LITE_ENSURE(context, affine_quantization);
|
||||
TF_LITE_ENSURE(context, affine_quantization->scale);
|
||||
TF_LITE_ENSURE(context, affine_quantization->zero_point);
|
||||
|
||||
TF_LITE_ENSURE(context,
|
||||
affine_quantization->scale->size == 1 ||
|
||||
affine_quantization->scale->size ==
|
||||
filter->dims->data[kConvQuantizedDimension]);
|
||||
TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
|
||||
affine_quantization->zero_point->size);
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(CalculateOpData(context, node, params, width, height,
|
||||
filter_width, filter_height,
|
||||
input->type, data));
|
||||
|
||||
// Offsets (zero points)
|
||||
data->params.input_offset = -input->params.zero_point;
|
||||
data->params.weights_offset = -filter->params.zero_point;
|
||||
data->params.output_offset = output->params.zero_point;
|
||||
|
||||
// Stride
|
||||
data->params.stride_width = params->stride_width;
|
||||
data->params.stride_height = params->stride_height;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(filter);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
const TfLiteEvalTensor* filter =
|
||||
tflite::micro::GetEvalInput(context, node, kFilterTensor);
|
||||
const TfLiteEvalTensor* bias =
|
||||
(NumInputs(node) == 4)
|
||||
? tflite::micro::GetEvalInput(context, node, kBiasTensor)
|
||||
: nullptr;
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
|
||||
TFLITE_DCHECK(node->user_data != nullptr);
|
||||
const OpData& data = *(static_cast<const OpData*>(node->user_data));
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, input->type, output->type);
|
||||
TF_LITE_ENSURE_MSG(
|
||||
context,
|
||||
input->type == filter->type ||
|
||||
(input->type == kTfLiteInt16 && filter->type == kTfLiteInt8),
|
||||
"Hybrid models are not supported on TFLite Micro.");
|
||||
|
||||
switch (input->type) { // Already know in/out types are same.
|
||||
case kTfLiteFloat32: {
|
||||
reference_ops::TransposeConv(
|
||||
data.params, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<float>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<float>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<float>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<float>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr);
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
int32_t* scratch_buffer = static_cast<int32_t*>(
|
||||
context->GetScratchBuffer(context, data.scratch_buffer_index));
|
||||
reference_integer_ops::TransposeConv(
|
||||
data.params, data.per_channel_output_multiplier,
|
||||
data.per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int8_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<int32_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int8_t>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr, scratch_buffer);
|
||||
break;
|
||||
}
|
||||
case kTfLiteInt16: {
|
||||
std::int64_t* scratch_buffer = static_cast<int64_t*>(
|
||||
context->GetScratchBuffer(context, data.scratch_buffer_index));
|
||||
// TODO(b/192090531): Remove this once all 8x16 transpose conv models use
|
||||
// 64-bit biases.
|
||||
if (bias != nullptr && bias->type == kTfLiteInt16) {
|
||||
std::int64_t* bias_converted_buffer =
|
||||
static_cast<int64_t*>(context->GetScratchBuffer(
|
||||
context, data.bias_converted_buffer_index));
|
||||
for (int i = 0; i < tflite::micro::GetTensorShape(bias).FlatSize();
|
||||
i++) {
|
||||
bias_converted_buffer[i] = bias->data.i16[i];
|
||||
}
|
||||
reference_integer_ops::TransposeConv(
|
||||
data.params, data.per_channel_output_multiplier,
|
||||
data.per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias), bias_converted_buffer,
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr, scratch_buffer);
|
||||
} else {
|
||||
reference_integer_ops::TransposeConv(
|
||||
data.params, data.per_channel_output_multiplier,
|
||||
data.per_channel_output_shift, tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorData<int16_t>(input),
|
||||
tflite::micro::GetTensorShape(filter),
|
||||
tflite::micro::GetTensorData<int8_t>(filter),
|
||||
tflite::micro::GetTensorShape(bias),
|
||||
tflite::micro::GetOptionalTensorData<std::int64_t>(bias),
|
||||
tflite::micro::GetTensorShape(output),
|
||||
tflite::micro::GetTensorData<int16_t>(output),
|
||||
tflite::micro::GetTensorShape(nullptr), nullptr, scratch_buffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_TRANSPOSE_CONV() {
|
||||
return tflite::micro::RegisterOp(Init, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,244 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_KERNELS_UNIDIRECTIONAL_SEQUENCE_LSTM_TEST_CONFIG_H_
|
||||
#define TENSORFLOW_LITE_MICRO_KERNELS_UNIDIRECTIONAL_SEQUENCE_LSTM_TEST_CONFIG_H_
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace testing {
|
||||
|
||||
// TODO(b/230666079) enable below tests for xtensa when the xtensa
|
||||
// kernel is reconciled with reference kernel
|
||||
#if !defined(XTENSA)
|
||||
|
||||
struct LstmIntegerTestConfig {
|
||||
const int n_batch;
|
||||
const int n_input;
|
||||
const int n_cell;
|
||||
const int n_output;
|
||||
const int sequence_length;
|
||||
const bool time_major;
|
||||
const bool use_cifg;
|
||||
const bool use_peephole;
|
||||
const bool use_projection_weights;
|
||||
const bool use_projection_bias;
|
||||
const bool use_layer_norm;
|
||||
const bool use_8x8_8_implementation;
|
||||
float intermediate_scale[5][2];
|
||||
int intermediate_zp[5][2];
|
||||
TfLiteAffineQuantization* intermediate_qparam;
|
||||
|
||||
const float* input;
|
||||
int8_t* input_quant;
|
||||
|
||||
const float* input_to_input_weights;
|
||||
int8_t* lstm_i2i_quant;
|
||||
const float* input_to_forget_weights;
|
||||
int8_t* lstm_i2f_quant;
|
||||
const float* input_to_cell_weights;
|
||||
int8_t* lstm_i2c_quant;
|
||||
const float* input_to_output_weights;
|
||||
int8_t* lstm_i2o_quant;
|
||||
|
||||
const float* recurrent_to_input_weights;
|
||||
int8_t* lstm_r2i_quant;
|
||||
const float* recurrent_to_forget_weights;
|
||||
int8_t* lstm_r2f_quant;
|
||||
const float* recurrent_to_cell_weights;
|
||||
int8_t* lstm_r2c_quant;
|
||||
const float* recurrent_to_output_weights;
|
||||
int8_t* lstm_r2o_quant;
|
||||
|
||||
const float* cell_to_input_weights;
|
||||
int16_t* lstm_c2i_quant;
|
||||
const float* cell_to_forget_weights;
|
||||
int16_t* lstm_c2f_quant;
|
||||
const float* cell_to_output_weights;
|
||||
int16_t* lstm_c2o_quant;
|
||||
|
||||
const float* input_gate_bias;
|
||||
int32_t* lstm_igate_bias_quant;
|
||||
const float* forget_gate_bias;
|
||||
int32_t* lstm_fgate_bias_quant;
|
||||
const float* cell_gate_bias;
|
||||
int32_t* lstm_cgate_bias_quant;
|
||||
const float* output_gate_bias;
|
||||
int32_t* lstm_ogate_bias_quant;
|
||||
|
||||
const float* projection_weights;
|
||||
int8_t* lstm_proj_w_quant;
|
||||
const float* projection_bias;
|
||||
int32_t* projection_bias_quant;
|
||||
|
||||
int16_t* output_state;
|
||||
int16_t* cell_state;
|
||||
|
||||
const float* input_layer_norm_coefficients;
|
||||
int16_t* lstm_input_layer_norm_coeff_quant;
|
||||
const float* forget_layer_norm_coefficients;
|
||||
int16_t* lstm_forget_layer_norm_coeff_quant;
|
||||
const float* cell_layer_norm_coefficients;
|
||||
int16_t* lstm_cell_layer_norm_coeff_quant;
|
||||
const float* output_layer_norm_coefficients;
|
||||
int16_t* lstm_output_layer_norm_coeff_quant;
|
||||
|
||||
int8_t* output;
|
||||
const int8_t* expected_output;
|
||||
|
||||
bool asymmetric_quantize_inputs;
|
||||
const float ranges[25][2];
|
||||
};
|
||||
|
||||
struct LstmFloatTestConfig {
|
||||
const int n_batch;
|
||||
const int n_input;
|
||||
const int n_cell;
|
||||
const int n_output;
|
||||
const int sequence_length;
|
||||
const bool time_major;
|
||||
const bool use_cifg;
|
||||
const bool use_peephole;
|
||||
const bool use_projection_weights;
|
||||
const bool use_projection_bias;
|
||||
const bool use_layer_norm;
|
||||
const float cell_clip;
|
||||
const float proj_clip;
|
||||
|
||||
const float* input_original;
|
||||
float* input;
|
||||
|
||||
const float* input_to_input_weights;
|
||||
const float* input_to_forget_weights;
|
||||
const float* input_to_cell_weights;
|
||||
const float* input_to_output_weights;
|
||||
|
||||
const float* recurrent_to_input_weights;
|
||||
const float* recurrent_to_forget_weights;
|
||||
const float* recurrent_to_cell_weights;
|
||||
const float* recurrent_to_output_weights;
|
||||
|
||||
const float* cell_to_input_weights;
|
||||
const float* cell_to_forget_weights;
|
||||
const float* cell_to_output_weights;
|
||||
|
||||
const float* input_gate_bias;
|
||||
const float* forget_gate_bias;
|
||||
const float* cell_gate_bias;
|
||||
const float* output_gate_bias;
|
||||
|
||||
const float* projection_weights;
|
||||
const float* projection_bias;
|
||||
|
||||
float* output_state;
|
||||
float* cell_state;
|
||||
|
||||
const float* input_layer_norm_coefficients;
|
||||
const float* forget_layer_norm_coefficients;
|
||||
const float* cell_layer_norm_coefficients;
|
||||
const float* output_layer_norm_coefficients;
|
||||
|
||||
float* output;
|
||||
const float* expected_output_original;
|
||||
float* expected_output;
|
||||
};
|
||||
|
||||
struct LstmWeightQuantizationBuffers {
|
||||
int8_t* lstm_i2i_quant;
|
||||
float* lstm_i2i_scale;
|
||||
int* lstm_i2i_zp;
|
||||
TfLiteAffineQuantization* lstm_i2i_qparam;
|
||||
|
||||
int8_t* lstm_i2f_quant;
|
||||
float* lstm_i2f_scale;
|
||||
int* lstm_i2f_zp;
|
||||
TfLiteAffineQuantization* lstm_i2f_qparam;
|
||||
|
||||
int8_t* lstm_i2c_quant;
|
||||
float* lstm_i2c_scale;
|
||||
int* lstm_i2c_zp;
|
||||
TfLiteAffineQuantization* lstm_i2c_qparam;
|
||||
|
||||
int8_t* lstm_i2o_quant;
|
||||
float* lstm_i2o_scale;
|
||||
int* lstm_i2o_zp;
|
||||
TfLiteAffineQuantization* lstm_i2o_qparam;
|
||||
|
||||
int8_t* lstm_r2i_quant;
|
||||
float* lstm_r2i_scale;
|
||||
int* lstm_r2i_zp;
|
||||
TfLiteAffineQuantization* lstm_r2i_qparam;
|
||||
|
||||
int8_t* lstm_r2f_quant;
|
||||
float* lstm_r2f_scale;
|
||||
int* lstm_r2f_zp;
|
||||
TfLiteAffineQuantization* lstm_r2f_qparam;
|
||||
|
||||
int8_t* lstm_r2c_quant;
|
||||
float* lstm_r2c_scale;
|
||||
int* lstm_r2c_zp;
|
||||
TfLiteAffineQuantization* lstm_r2c_qparam;
|
||||
|
||||
int8_t* lstm_r2o_quant;
|
||||
float* lstm_r2o_scale;
|
||||
int* lstm_r2o_zp;
|
||||
TfLiteAffineQuantization* lstm_r2o_qparam;
|
||||
|
||||
int8_t* lstm_c2i_quant;
|
||||
float* lstm_c2i_scale;
|
||||
int* lstm_c2i_zp;
|
||||
TfLiteAffineQuantization* lstm_c2i_qparam;
|
||||
|
||||
int8_t* lstm_c2f_quant;
|
||||
float* lstm_c2f_scale;
|
||||
int* lstm_c2f_zp;
|
||||
TfLiteAffineQuantization* lstm_c2f_qparam;
|
||||
|
||||
int8_t* lstm_c2o_quant;
|
||||
float* lstm_c2o_scale;
|
||||
int* lstm_c2o_zp;
|
||||
TfLiteAffineQuantization* lstm_c2o_qparam;
|
||||
|
||||
int8_t* lstm_proj_w_quant;
|
||||
float* lstm_proj_w_scale;
|
||||
int* lstm_proj_w_zp;
|
||||
TfLiteAffineQuantization* lstm_proj_w_qparam;
|
||||
};
|
||||
|
||||
extern LstmIntegerTestConfig lstm_integer_no_peephole_config;
|
||||
|
||||
extern LstmIntegerTestConfig lstm_integer_peephole_config;
|
||||
|
||||
extern LstmFloatTestConfig lstm_no_cifg_no_peephole_no_proj_config;
|
||||
|
||||
extern LstmFloatTestConfig lstm_cifg_peephole_no_proj_config;
|
||||
|
||||
extern LstmFloatTestConfig lstm_no_cifg_peephole_proj_config;
|
||||
|
||||
extern LstmFloatTestConfig lstm_no_cifg_peephole_proj_bias_config;
|
||||
|
||||
extern LstmWeightQuantizationBuffers lstm_no_cifg_no_peephole_no_proj_buffers;
|
||||
|
||||
extern LstmWeightQuantizationBuffers lstm_cifg_peephole_no_proj_buffers;
|
||||
|
||||
extern LstmWeightQuantizationBuffers lstm_no_cifg_peephole_proj_buffers;
|
||||
|
||||
extern LstmFloatTestConfig cifg_peephole_no_proj_config_layer_norm;
|
||||
|
||||
#endif // !defined(XTENSA)
|
||||
} // namespace testing
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_KERNELS_UNIDIRECTIONAL_SEQUENCE_LSTM_TEST_CONFIG_H_
|
||||
@@ -1,111 +0,0 @@
|
||||
/* 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/builtin_op_data.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace ops {
|
||||
namespace micro {
|
||||
namespace unpack {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
|
||||
template <typename T>
|
||||
TfLiteStatus UnpackImpl(TfLiteContext* context, TfLiteNode* node,
|
||||
const TfLiteEvalTensor* input, int output_count,
|
||||
int axis) {
|
||||
const TfLiteEvalTensor* output0 =
|
||||
tflite::micro::GetEvalOutput(context, node, 0);
|
||||
const TfLiteIntArray* input_dims = input->dims;
|
||||
const TfLiteIntArray* output_dims = output0->dims;
|
||||
const int dimensions = input_dims->size;
|
||||
|
||||
if (axis < 0) {
|
||||
axis += input->dims->size;
|
||||
}
|
||||
|
||||
TFLITE_DCHECK_LT(axis, dimensions);
|
||||
|
||||
int outer_size = 1;
|
||||
for (int i = 0; i < axis; ++i) {
|
||||
outer_size *= input_dims->data[i];
|
||||
}
|
||||
int copy_size = 1;
|
||||
for (int i = axis + 1; i < dimensions; ++i) {
|
||||
copy_size *= input_dims->data[i];
|
||||
}
|
||||
int output_size = 1;
|
||||
for (int i = 0; i < output_dims->size; ++i) {
|
||||
output_size *= output_dims->data[i];
|
||||
}
|
||||
TFLITE_DCHECK_EQ(output_size, copy_size * outer_size);
|
||||
|
||||
const T* input_data = tflite::micro::GetTensorData<T>(input);
|
||||
|
||||
for (int i = 0; i < output_count; ++i) {
|
||||
TfLiteEvalTensor* t = tflite::micro::GetEvalOutput(context, node, i);
|
||||
T* output_data = tflite::micro::GetTensorData<T>(t);
|
||||
for (int k = 0; k < outer_size; ++k) {
|
||||
T* output_ptr = output_data + copy_size * k;
|
||||
int loc = k * output_count * copy_size + i * copy_size;
|
||||
const T* input_ptr = input_data + loc;
|
||||
for (int j = 0; j < copy_size; ++j) output_ptr[j] = input_ptr[j];
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
TfLiteUnpackParams* data =
|
||||
reinterpret_cast<TfLiteUnpackParams*>(node->builtin_data);
|
||||
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
|
||||
switch (input->type) {
|
||||
case kTfLiteFloat32: {
|
||||
return UnpackImpl<float>(context, node, input, data->num, data->axis);
|
||||
}
|
||||
case kTfLiteInt32: {
|
||||
return UnpackImpl<int32_t>(context, node, input, data->num, data->axis);
|
||||
}
|
||||
case kTfLiteInt8: {
|
||||
return UnpackImpl<int8_t>(context, node, input, data->num, data->axis);
|
||||
}
|
||||
default: {
|
||||
MicroPrintf("Type '%s' is not supported by unpack.",
|
||||
TfLiteTypeGetName(input->type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace unpack
|
||||
|
||||
TfLiteRegistration Register_UNPACK() {
|
||||
return tflite::micro::RegisterOp(nullptr, nullptr, unpack::Eval);
|
||||
}
|
||||
|
||||
} // namespace micro
|
||||
} // namespace ops
|
||||
} // namespace tflite
|
||||
@@ -1,87 +0,0 @@
|
||||
/* Copyright 2021 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"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/kernels/kernel_util.h"
|
||||
|
||||
namespace tflite {
|
||||
namespace {
|
||||
|
||||
constexpr int kInputTensor = 0;
|
||||
constexpr int kOutputTensor = 0;
|
||||
|
||||
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
|
||||
MicroContext* micro_context = GetMicroContext(context);
|
||||
|
||||
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
|
||||
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
|
||||
TfLiteTensor* input =
|
||||
micro_context->AllocateTempInputTensor(node, kInputTensor);
|
||||
TF_LITE_ENSURE(context, input != nullptr);
|
||||
TfLiteTensor* output =
|
||||
micro_context->AllocateTempOutputTensor(node, kOutputTensor);
|
||||
TF_LITE_ENSURE(context, output != nullptr);
|
||||
output->type = input->type;
|
||||
|
||||
micro_context->DeallocateTempTfLiteTensor(input);
|
||||
micro_context->DeallocateTempTfLiteTensor(output);
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void resetZeros(T* out, const int num_elements) {
|
||||
for (int i = 0; i < num_elements; ++i) {
|
||||
out[i] = static_cast<T>(0);
|
||||
}
|
||||
}
|
||||
|
||||
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
|
||||
const TfLiteEvalTensor* input =
|
||||
tflite::micro::GetEvalInput(context, node, kInputTensor);
|
||||
TfLiteEvalTensor* output =
|
||||
tflite::micro::GetEvalOutput(context, node, kOutputTensor);
|
||||
int flat_size = MatchingFlatSize(tflite::micro::GetTensorShape(input),
|
||||
tflite::micro::GetTensorShape(output));
|
||||
switch (input->type) {
|
||||
case kTfLiteInt64:
|
||||
resetZeros(tflite::micro::GetTensorData<int64_t>(output), flat_size);
|
||||
break;
|
||||
case kTfLiteInt32:
|
||||
resetZeros(tflite::micro::GetTensorData<int32_t>(output), flat_size);
|
||||
break;
|
||||
case kTfLiteInt8:
|
||||
resetZeros(tflite::micro::GetTensorData<int8_t>(output), flat_size);
|
||||
break;
|
||||
case kTfLiteFloat32:
|
||||
resetZeros(tflite::micro::GetTensorData<float>(output), flat_size);
|
||||
break;
|
||||
default:
|
||||
MicroPrintf(
|
||||
"ZerosLike only currently supports int64, int32, "
|
||||
"and float32, got %d.",
|
||||
input->type);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TfLiteRegistration Register_ZEROS_LIKE() {
|
||||
return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,355 +0,0 @@
|
||||
/* Copyright 2022 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/micro/micro_allocation_info.h"
|
||||
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
constexpr char kOfflineMemAllocMetadata[] = "OfflineMemoryAllocation";
|
||||
constexpr int kUninitializedLifetime = -1;
|
||||
} // namespace
|
||||
|
||||
// Mark the given Allocation info as first created at the specified allocation
|
||||
// scope count. Only the first creation must be recorded since the allocation
|
||||
// scope count monotonically increases throughout the lifetime marking process.
|
||||
void AllocationInfoBuilder::UpdateFirstCreated(AllocationInfo* current,
|
||||
int allocation_scope_count) {
|
||||
TFLITE_DCHECK(current->first_created <= allocation_scope_count);
|
||||
if (current->first_created == kUninitializedLifetime) {
|
||||
current->first_created = allocation_scope_count;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the given AllocationInfo as last used at the specified allocation scope
|
||||
// count. Update the last used marker every time, since the allocation scope
|
||||
// count monotonically increases through the lifetime marking process.
|
||||
void AllocationInfoBuilder::UpdateLastUsed(AllocationInfo* current,
|
||||
int allocation_scope_count) {
|
||||
TFLITE_DCHECK(current->last_used <= allocation_scope_count);
|
||||
current->last_used = allocation_scope_count;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::MarkSubgraphLifetimesIfNecessary(
|
||||
const Operator* op, internal::ScratchBufferRequest* scratch_buffer_requests,
|
||||
ScratchBufferHandle* scratch_buffer_handles,
|
||||
SubgraphAllocations* allocations) {
|
||||
int first_subgraph_index = -1;
|
||||
int second_subgraph_index = -1;
|
||||
const OperatorCode* opcode =
|
||||
model_->operator_codes()->Get(op->opcode_index());
|
||||
switch (opcode->builtin_code()) {
|
||||
case BuiltinOperator_IF: {
|
||||
first_subgraph_index =
|
||||
op->builtin_options_as_IfOptions()->then_subgraph_index();
|
||||
second_subgraph_index =
|
||||
op->builtin_options_as_IfOptions()->else_subgraph_index();
|
||||
break;
|
||||
}
|
||||
case BuiltinOperator_CALL_ONCE: {
|
||||
first_subgraph_index =
|
||||
op->builtin_options_as_CallOnceOptions()->init_subgraph_index();
|
||||
break;
|
||||
}
|
||||
case BuiltinOperator_WHILE: {
|
||||
first_subgraph_index =
|
||||
op->builtin_options_as_WhileOptions()->cond_subgraph_index();
|
||||
second_subgraph_index =
|
||||
op->builtin_options_as_WhileOptions()->body_subgraph_index();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first_subgraph_index != -1) {
|
||||
// Enter a new allocation scope for each subgraph.
|
||||
allocation_scope_count_++;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
MarkAllocationLifetimes(first_subgraph_index, scratch_buffer_requests,
|
||||
scratch_buffer_handles, allocations));
|
||||
}
|
||||
if (second_subgraph_index != -1) {
|
||||
// Enter a new allocation scope for each subgraph.
|
||||
allocation_scope_count_++;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
MarkAllocationLifetimes(second_subgraph_index, scratch_buffer_requests,
|
||||
scratch_buffer_handles, allocations));
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::CreateAllocationInfo(
|
||||
int scratch_buffer_request_count) {
|
||||
size_t subgraph_offsets_length = model_->subgraphs()->size() * sizeof(size_t);
|
||||
info_.subgraph_offsets =
|
||||
reinterpret_cast<size_t*>(non_persistent_allocator_->AllocateTemp(
|
||||
subgraph_offsets_length, alignof(size_t)));
|
||||
if (info_.subgraph_offsets == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
reporter_,
|
||||
"Failed to allocate memory for memory planning, %d bytes required",
|
||||
subgraph_offsets_length);
|
||||
return kTfLiteError;
|
||||
}
|
||||
size_t tensor_count = 0;
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model_->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
// Add all tensors in each subgraph to the AllocationInfo array. Even weight
|
||||
// tensors are added but marked with needs_allocating = false. Including all
|
||||
// tensors in the graph here simplifies logic.
|
||||
info_.subgraph_offsets[subgraph_idx] = tensor_count;
|
||||
tensor_count += model_->subgraphs()->Get(subgraph_idx)->tensors()->size();
|
||||
}
|
||||
info_.tensor_count = tensor_count;
|
||||
|
||||
// Scratch buffer allocations follow tensor allocations, so the scratch offset
|
||||
// is equal to the number of tensor allocations.
|
||||
info_.scratch_offset = tensor_count;
|
||||
info_.allocation_info_count = tensor_count + scratch_buffer_request_count;
|
||||
info_.scratch_buffer_count = scratch_buffer_request_count;
|
||||
size_t bytes = sizeof(AllocationInfo) * info_.allocation_info_count;
|
||||
|
||||
// Allocate an array of AllocationInfo structs from the temp section. This
|
||||
// struct will be used by AllocationInfoBuilder to find buffer usage.
|
||||
info_.allocation_info = reinterpret_cast<AllocationInfo*>(
|
||||
non_persistent_allocator_->AllocateTemp(bytes, alignof(AllocationInfo)));
|
||||
if (info_.allocation_info == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
reporter_,
|
||||
"Failed to allocate memory for memory planning, %d bytes required",
|
||||
bytes);
|
||||
return kTfLiteError;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::FreeAllocationInfo() {
|
||||
non_persistent_allocator_->DeallocateTemp(
|
||||
reinterpret_cast<uint8_t*>(info_.allocation_info));
|
||||
non_persistent_allocator_->DeallocateTemp(
|
||||
reinterpret_cast<uint8_t*>(info_.subgraph_offsets));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::ValidateSubgraph(
|
||||
const SubGraph* subgraph, TfLiteEvalTensor* eval_tensors) {
|
||||
uint32_t operators_size = NumSubgraphOperators(subgraph);
|
||||
|
||||
for (uint32_t i = 0; i < operators_size; i++) {
|
||||
const auto op = subgraph->operators()->Get(i);
|
||||
for (size_t n = 0;
|
||||
op->intermediates() != nullptr && n < op->intermediates()->size();
|
||||
n++) {
|
||||
const int tensor_index = op->intermediates()->Get(n);
|
||||
size_t tensor_size = -1;
|
||||
TF_LITE_ENSURE_STATUS(TfLiteEvalTensorByteLength(
|
||||
&eval_tensors[tensor_index], &tensor_size));
|
||||
if (tensor_size != 0) {
|
||||
MicroPrintf(
|
||||
"Does not support intermediate tensor with non-zero size: %d",
|
||||
tensor_size);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::InitializeAllocationInfo(
|
||||
const int32_t* offline_offsets, SubgraphAllocations* allocations) {
|
||||
AllocationInfo* allocation_info = info_.allocation_info;
|
||||
// Initialize allocation info for every tensor in every subgraph.
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model_->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
const SubGraph* subgraph = model_->subgraphs()->Get(subgraph_idx);
|
||||
TfLiteEvalTensor* eval_tensors = allocations[subgraph_idx].tensors;
|
||||
AllocationInfo* subgraph_allocation_info =
|
||||
&allocation_info[info_.subgraph_offsets[subgraph_idx]];
|
||||
|
||||
// Ensure constraints are met.
|
||||
TF_LITE_ENSURE_STATUS(ValidateSubgraph(subgraph, eval_tensors));
|
||||
|
||||
for (size_t i = 0; i < subgraph->tensors()->size(); ++i) {
|
||||
AllocationInfo* current = &subgraph_allocation_info[i];
|
||||
current->output_ptr = &(eval_tensors[i].data.data);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TfLiteEvalTensorByteLength(&eval_tensors[i], ¤t->bytes));
|
||||
|
||||
current->first_created = kUninitializedLifetime;
|
||||
current->last_used = kUninitializedLifetime;
|
||||
current->needs_allocating =
|
||||
(eval_tensors[i].data.data == nullptr) &&
|
||||
(!subgraph->tensors()->Get(i)->is_variable()) &&
|
||||
(current->bytes != 0);
|
||||
if (offline_offsets) {
|
||||
current->offline_offset = offline_offsets[i];
|
||||
} else {
|
||||
current->offline_offset = kOnlinePlannedBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Initialize allocation info for every scratch buffer.
|
||||
AllocationInfo* scratch_allocation_info =
|
||||
&allocation_info[info_.scratch_offset];
|
||||
for (size_t i = 0; i < info_.scratch_buffer_count; i++) {
|
||||
AllocationInfo* current = &scratch_allocation_info[i];
|
||||
current->first_created = kUninitializedLifetime;
|
||||
current->last_used = kUninitializedLifetime;
|
||||
current->needs_allocating = true;
|
||||
current->offline_offset = kOnlinePlannedBuffer;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AllocationInfoBuilder::MarkAllocationLifetimes(
|
||||
int subgraph_idx, internal::ScratchBufferRequest* scratch_buffer_requests,
|
||||
ScratchBufferHandle* scratch_buffer_handles,
|
||||
SubgraphAllocations* allocations) {
|
||||
const SubGraph* subgraph = model_->subgraphs()->Get(subgraph_idx);
|
||||
|
||||
AllocationInfo* allocation_info = info_.allocation_info;
|
||||
// Each subgraph's tensor allocations are in a contiguous block starting at
|
||||
// subgraph_offsets_[subgraph index] with one entry per tensor.
|
||||
AllocationInfo* subgraph_allocation_info =
|
||||
&allocation_info[info_.subgraph_offsets[subgraph_idx]];
|
||||
|
||||
uint32_t operators_size = NumSubgraphOperators(subgraph);
|
||||
// Mark all inputs as created at the start of the subgraph invocation.
|
||||
for (size_t i = 0;
|
||||
subgraph->inputs() != nullptr && i < subgraph->inputs()->size(); ++i) {
|
||||
const int tensor_index = subgraph->inputs()->Get(i);
|
||||
AllocationInfo* current = &subgraph_allocation_info[tensor_index];
|
||||
UpdateFirstCreated(current, allocation_scope_count_);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < operators_size; i++) {
|
||||
// Each operator has a new allocation scope.
|
||||
allocation_scope_count_++;
|
||||
const auto* op = subgraph->operators()->Get(i);
|
||||
// Figure out when the first creation and use of each tensor is.
|
||||
for (size_t n = 0; op->outputs() != nullptr && n < op->outputs()->size();
|
||||
++n) {
|
||||
const int tensor_index = op->outputs()->Get(n);
|
||||
AllocationInfo* current = &subgraph_allocation_info[tensor_index];
|
||||
UpdateFirstCreated(current, allocation_scope_count_);
|
||||
}
|
||||
|
||||
// Keep track of scope count before any subgraphs, so that scratch buffers'
|
||||
// lifetime within a control flow op properly overlaps with all subgraphs.
|
||||
int start_allocation_scope_count = allocation_scope_count_;
|
||||
|
||||
// Control flow operators can invoke subgraphs. Plan these subgraphs
|
||||
// before continuing on to the rest of the graph.
|
||||
MarkSubgraphLifetimesIfNecessary(op, scratch_buffer_requests,
|
||||
scratch_buffer_handles, allocations);
|
||||
|
||||
// Figure out when the last use of each tensor is.
|
||||
for (size_t n = 0; op->inputs() != nullptr && n < op->inputs()->size();
|
||||
++n) {
|
||||
const int tensor_index = op->inputs()->Get(n);
|
||||
// Optional bias tensors can have an index of -1 when they are omitted.
|
||||
if (tensor_index >= 0) {
|
||||
AllocationInfo* current = &subgraph_allocation_info[tensor_index];
|
||||
// No need to update creation since it is either marked by the subgraph
|
||||
// or producer op, or it is not part of the memory plan (weight, bias
|
||||
// tensor).
|
||||
UpdateLastUsed(current, allocation_scope_count_);
|
||||
}
|
||||
}
|
||||
for (size_t n = 0; op->outputs() != nullptr && n < op->outputs()->size();
|
||||
++n) {
|
||||
const int tensor_index = op->outputs()->Get(n);
|
||||
AllocationInfo* current = &subgraph_allocation_info[tensor_index];
|
||||
UpdateLastUsed(current, allocation_scope_count_);
|
||||
}
|
||||
|
||||
// Mark thse lifetime of scratch buffers belonging to the current node. This
|
||||
// operation is O(N * M) where N is the total number of visited nodes and M
|
||||
// is the total number of scratch buffers.
|
||||
// TODO(b/217794030): Optimize this memory planning code.
|
||||
AllocationInfo* scratch_allocation_info =
|
||||
&allocation_info[info_.scratch_offset];
|
||||
for (size_t scratch_idx = 0; scratch_idx < info_.scratch_buffer_count;
|
||||
scratch_idx++) {
|
||||
internal::ScratchBufferRequest request =
|
||||
scratch_buffer_requests[scratch_idx];
|
||||
AllocationInfo* current = &scratch_allocation_info[scratch_idx];
|
||||
if (request.node_idx == static_cast<int>(i) &&
|
||||
request.subgraph_idx == static_cast<int>(subgraph_idx)) {
|
||||
ScratchBufferHandle* current_handle =
|
||||
&(scratch_buffer_handles[scratch_idx]);
|
||||
current->output_ptr = reinterpret_cast<void**>(¤t_handle->data);
|
||||
current->bytes = request.bytes;
|
||||
UpdateFirstCreated(current, start_allocation_scope_count);
|
||||
UpdateLastUsed(current, allocation_scope_count_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all outputs as persistent to the end of the subgraph invocation.
|
||||
for (size_t i = 0;
|
||||
subgraph->outputs() != nullptr && i < subgraph->outputs()->size(); ++i) {
|
||||
const int tensor_index = subgraph->outputs()->Get(i);
|
||||
AllocationInfo* current = &subgraph_allocation_info[tensor_index];
|
||||
UpdateLastUsed(current, allocation_scope_count_);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Get offline tensors allocation plan. See
|
||||
// micro/docs/memory_management.md for more info.
|
||||
TfLiteStatus AllocationInfoBuilder::GetOfflinePlannedOffsets(
|
||||
const int32_t** offline_planner_offsets) {
|
||||
if (model_->metadata()) {
|
||||
for (size_t i = 0; i < model_->metadata()->size(); ++i) {
|
||||
auto metadata = model_->metadata()->Get(i);
|
||||
const size_t metadata_name_size = (size_t)metadata->name()->size();
|
||||
|
||||
if ((strncmp(metadata->name()->c_str(), kOfflineMemAllocMetadata,
|
||||
std::min(metadata_name_size,
|
||||
strlen(kOfflineMemAllocMetadata))) == 0) &&
|
||||
metadata_name_size == strlen(kOfflineMemAllocMetadata)) {
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers =
|
||||
model_->buffers();
|
||||
auto* buffer = (*buffers)[metadata->buffer()];
|
||||
auto* array = buffer->data();
|
||||
const uint32_t* metadata_buffer =
|
||||
reinterpret_cast<const uint32_t*>(array->data());
|
||||
const size_t nbr_tensors = static_cast<size_t>(metadata_buffer[2]);
|
||||
*offline_planner_offsets =
|
||||
reinterpret_cast<const int32_t*>(&metadata_buffer[3]);
|
||||
|
||||
if (info_.tensor_count != nbr_tensors) {
|
||||
TF_LITE_REPORT_ERROR(reporter_,
|
||||
"Nbr of offline buffer offsets (%d) in metadata "
|
||||
"not equal nbr tensors (%d)\n",
|
||||
nbr_tensors, info_.tensor_count);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,965 +0,0 @@
|
||||
/* Copyright 2020 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/micro/micro_allocator.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "flatbuffers/flatbuffers.h" // from @flatbuffers
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/core/api/flatbuffer_conversions.h"
|
||||
#include "tensorflow/lite/core/api/op_resolver.h"
|
||||
#include "tensorflow/lite/core/api/tensor_utils.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/non_persistent_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/persistent_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/single_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
#include "tensorflow/lite/micro/flatbuffer_utils.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h"
|
||||
#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h"
|
||||
#include "tensorflow/lite/micro/micro_allocation_info.h"
|
||||
#include "tensorflow/lite/micro/micro_arena_constants.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/schema/schema_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
||||
// Maximum number of scratch buffer requests per operator. Operator kernels that
|
||||
// request more than this value will receive an exception.
|
||||
constexpr size_t kMaxScratchBuffersPerOp = 12;
|
||||
|
||||
// Sentinel value used as a placeholder to mark a ScratchBufferRequest request
|
||||
// needs a node id assignment.
|
||||
constexpr int kUnassignedScratchBufferRequestIndex = -1;
|
||||
|
||||
const TfLiteIntArray kZeroLengthIntArray = {};
|
||||
|
||||
class MicroBuiltinDataAllocator : public BuiltinDataAllocator {
|
||||
public:
|
||||
explicit MicroBuiltinDataAllocator(
|
||||
IPersistentBufferAllocator* persistent_allocator)
|
||||
: persistent_allocator_(persistent_allocator) {}
|
||||
|
||||
void* Allocate(size_t size, size_t alignment_hint) override {
|
||||
return persistent_allocator_->AllocatePersistentBuffer(size,
|
||||
alignment_hint);
|
||||
}
|
||||
void Deallocate(void* data) override {
|
||||
// Do not deallocate, builtin data needs to be available for the life time
|
||||
// of the model.
|
||||
}
|
||||
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE
|
||||
|
||||
private:
|
||||
IPersistentBufferAllocator* persistent_allocator_;
|
||||
};
|
||||
|
||||
TfLiteStatus CreatePlan(ErrorReporter* error_reporter,
|
||||
MicroMemoryPlanner* planner,
|
||||
const AllocationInfo* allocation_info,
|
||||
size_t allocation_info_size) {
|
||||
// Add the tensors to our allocation plan.
|
||||
for (size_t i = 0; i < allocation_info_size; ++i) {
|
||||
const AllocationInfo* current = &allocation_info[i];
|
||||
if (current->needs_allocating) {
|
||||
size_t aligned_bytes_required =
|
||||
AlignSizeUp(current->bytes, MicroArenaBufferAlignment());
|
||||
if (current->offline_offset == kOnlinePlannedBuffer) {
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
planner->AddBuffer(error_reporter, aligned_bytes_required,
|
||||
current->first_created, current->last_used));
|
||||
} else {
|
||||
TF_LITE_ENSURE_STATUS(planner->AddBuffer(
|
||||
error_reporter, aligned_bytes_required, current->first_created,
|
||||
current->last_used, current->offline_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus CommitPlan(ErrorReporter* error_reporter,
|
||||
MicroMemoryPlanner* planner, uint8_t* starting_point,
|
||||
const AllocationInfo* allocation_info,
|
||||
size_t allocation_info_size) {
|
||||
// Figure out the actual memory addresses for each buffer, based on the plan.
|
||||
int planner_index = 0;
|
||||
for (size_t i = 0; i < allocation_info_size; ++i) {
|
||||
const AllocationInfo* current = &allocation_info[i];
|
||||
if (current->needs_allocating) {
|
||||
int offset = -1;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
planner->GetOffsetForBuffer(error_reporter, planner_index, &offset));
|
||||
*current->output_ptr = reinterpret_cast<void*>(starting_point + offset);
|
||||
++planner_index;
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
IPersistentBufferAllocator* CreatePersistentArenaAllocator(uint8_t* buffer_head,
|
||||
size_t buffer_size) {
|
||||
// Align the actually used area by the tail because persistent buffer grows
|
||||
// from the bottom to top.
|
||||
uint8_t* aligned_buffer_tail =
|
||||
AlignPointerDown(buffer_head + buffer_size, MicroArenaBufferAlignment());
|
||||
size_t aligned_buffer_size = aligned_buffer_tail - buffer_head;
|
||||
PersistentArenaBufferAllocator tmp =
|
||||
PersistentArenaBufferAllocator(buffer_head, aligned_buffer_size);
|
||||
|
||||
// Allocate enough bytes from the buffer to create a
|
||||
// SingleArenaBufferAllocator. The new instance will use the current adjusted
|
||||
// tail buffer from the tmp allocator instance.
|
||||
uint8_t* allocator_buffer =
|
||||
tmp.AllocatePersistentBuffer(sizeof(PersistentArenaBufferAllocator),
|
||||
alignof(PersistentArenaBufferAllocator));
|
||||
// Use the default copy constructor to populate internal states.
|
||||
return new (allocator_buffer) PersistentArenaBufferAllocator(tmp);
|
||||
}
|
||||
|
||||
// NonPersistentBufferAllocator instance is created in the persistent buffer
|
||||
// because it has to be persistent to keep track of the non-persistent buffer
|
||||
// information.
|
||||
INonPersistentBufferAllocator* CreateNonPersistentArenaAllocator(
|
||||
uint8_t* buffer_head, size_t buffer_size,
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator) {
|
||||
uint8_t* allocator_buffer =
|
||||
persistent_buffer_allocator->AllocatePersistentBuffer(
|
||||
sizeof(NonPersistentArenaBufferAllocator),
|
||||
alignof(NonPersistentArenaBufferAllocator));
|
||||
// Align the actually used area by the head because persistent buffer grows
|
||||
// from the head to bottom.
|
||||
uint8_t* aligned_buffer_head =
|
||||
AlignPointerUp(buffer_head, MicroArenaBufferAlignment());
|
||||
size_t aligned_buffer_size = buffer_head + buffer_size - aligned_buffer_head;
|
||||
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator =
|
||||
new (allocator_buffer) NonPersistentArenaBufferAllocator(
|
||||
aligned_buffer_head, aligned_buffer_size);
|
||||
return non_persistent_buffer_allocator;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Returns a pointer to any buffer associated with the flatbuffer tensor. Can
|
||||
// return nullptr if no buffer is found.
|
||||
void* GetFlatbufferTensorBuffer(
|
||||
const tflite::Tensor& flatbuffer_tensor,
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers) {
|
||||
// We need to figure out where the actual contents of this tensor are stored
|
||||
// in memory. We'll check to see if there's a serialized buffer (pretty much
|
||||
// the same as a constant op in TensorFlow) associated with this tensor first,
|
||||
// and if there is update the runtime structure to point to its location in
|
||||
// memory.
|
||||
// First see if there's any buffer information in the serialized tensor.
|
||||
// TODO(b/170379532): Add better unit tests to validate flatbuffer values.
|
||||
void* out_buffer = nullptr;
|
||||
if (auto* buffer = (*buffers)[flatbuffer_tensor.buffer()]) {
|
||||
// If we've found a buffer, does it have any data?
|
||||
if (auto* array = buffer->data()) {
|
||||
// If it has any data, is the data size larger than zero?
|
||||
if (array->size()) {
|
||||
// We've found a buffer with valid data, so update the runtime tensor
|
||||
// data structure to point to it.
|
||||
out_buffer = const_cast<void*>(static_cast<const void*>(array->data()));
|
||||
}
|
||||
}
|
||||
// TODO(petewarden): It's not clear in what circumstances we could have a
|
||||
// buffer in the serialized tensor, but it doesn't have any data in it. Is
|
||||
// that a validly-generated file, and if so what does it mean, or is it an
|
||||
// error condition? It would be good to tighten up the specification to make
|
||||
// it less ambiguous.
|
||||
}
|
||||
return out_buffer;
|
||||
}
|
||||
|
||||
TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator,
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator,
|
||||
bool allocate_temp, const tflite::Tensor& flatbuffer_tensor,
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
|
||||
ErrorReporter* error_reporter, TfLiteTensor* result) {
|
||||
TFLITE_DCHECK(result != nullptr);
|
||||
|
||||
*result = {};
|
||||
// Make sure the serialized type is one we know how to deal with, and convert
|
||||
// it from a flatbuffer enum into a constant used by the kernel C API.
|
||||
TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
|
||||
&result->type, error_reporter));
|
||||
// Make sure we remember if the serialized tensor is designated as a variable.
|
||||
result->is_variable = flatbuffer_tensor.is_variable();
|
||||
|
||||
result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
|
||||
|
||||
// TODO(petewarden): Some of these paths aren't getting enough testing
|
||||
// coverage, so we should figure out some tests that exercise them.
|
||||
if (result->data.data == nullptr) {
|
||||
// The tensor contents haven't been set from a serialized buffer, so
|
||||
// make a note that they will be allocated from memory. The actual
|
||||
// allocation won't happen until later.
|
||||
result->allocation_type = kTfLiteArenaRw;
|
||||
} else {
|
||||
// We set the data from a serialized buffer, so record tha.
|
||||
result->allocation_type = kTfLiteMmapRo;
|
||||
}
|
||||
|
||||
// Figure out what the size in bytes of the buffer is and store it.
|
||||
size_t type_size;
|
||||
TF_LITE_ENSURE_STATUS(BytesRequiredForTensor(
|
||||
flatbuffer_tensor, &result->bytes, &type_size, error_reporter));
|
||||
|
||||
if (flatbuffer_tensor.shape() == nullptr) {
|
||||
// flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
|
||||
// tensor.
|
||||
// TODO(b/188459715): figure out why const_cast is required here.
|
||||
result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
|
||||
} else {
|
||||
// TFLM doesn't allow reshaping the tensor which requires dynamic memory
|
||||
// allocation so it is safe to drop the const qualifier. In the future, if
|
||||
// we really want to update the tensor shape, we can always pass in a new
|
||||
// TfLiteIntArray - especially we have to do so if the dimension is
|
||||
result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape());
|
||||
}
|
||||
|
||||
// Copy the quantization information from the serialized data.
|
||||
const auto* src_quantization = flatbuffer_tensor.quantization();
|
||||
if (src_quantization && src_quantization->scale() &&
|
||||
(src_quantization->scale()->size() > 0) &&
|
||||
src_quantization->zero_point() &&
|
||||
(src_quantization->zero_point()->size() > 0)) {
|
||||
// Always populate the TfLiteTensor.params field, even if there are
|
||||
// per-channel quantization parameters.
|
||||
result->params.scale = src_quantization->scale()->Get(0);
|
||||
// Note that the zero_point field in the FlatBuffers schema is a 64-bit
|
||||
// integer, but the zero_point field in the TfLiteQuantizationParams struct
|
||||
// is a 32-bit integer.
|
||||
result->params.zero_point =
|
||||
static_cast<int32_t>(src_quantization->zero_point()->Get(0));
|
||||
|
||||
// Populate per-channel quantization params.
|
||||
int channels = src_quantization->scale()->size();
|
||||
TfLiteAffineQuantization* quantization =
|
||||
allocate_temp
|
||||
? reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
non_persistent_buffer_allocator->AllocateTemp(
|
||||
sizeof(TfLiteAffineQuantization),
|
||||
alignof(TfLiteAffineQuantization)))
|
||||
: reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
persistent_buffer_allocator->AllocatePersistentBuffer(
|
||||
sizeof(TfLiteAffineQuantization),
|
||||
alignof(TfLiteAffineQuantization)));
|
||||
if (quantization == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter,
|
||||
"Unable to allocate TfLiteAffineQuantization.\n");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// TODO(b/153688719): Reduce tail allocation by using a global zero-point
|
||||
// buffer. This value can not be reused from the flatbuffer since the
|
||||
// zero_point is stored as a int64_t.
|
||||
quantization->zero_point =
|
||||
allocate_temp
|
||||
? reinterpret_cast<TfLiteIntArray*>(
|
||||
non_persistent_buffer_allocator->AllocateTemp(
|
||||
TfLiteIntArrayGetSizeInBytes(channels),
|
||||
alignof(TfLiteIntArray)))
|
||||
: reinterpret_cast<TfLiteIntArray*>(
|
||||
persistent_buffer_allocator->AllocatePersistentBuffer(
|
||||
TfLiteIntArrayGetSizeInBytes(channels),
|
||||
alignof(TfLiteIntArray)));
|
||||
if (quantization->zero_point == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter,
|
||||
"Unable to allocate quantization->zero_point.\n");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
quantization->scale =
|
||||
FlatBufferVectorToTfLiteTypeArray(src_quantization->scale());
|
||||
|
||||
quantization->zero_point->size = channels;
|
||||
int* zero_point_data = quantization->zero_point->data;
|
||||
for (int i = 0; i < channels; i++) {
|
||||
// As a space-saving optimization, zero point arrays for weights can be
|
||||
// reduced to a single value, since all zero points for weights are 0.
|
||||
zero_point_data[i] = src_quantization->zero_point()->size() ==
|
||||
src_quantization->scale()->size()
|
||||
? src_quantization->zero_point()->Get(i)
|
||||
: src_quantization->zero_point()->Get(0);
|
||||
}
|
||||
// TODO(rocky): Need to add a micro_allocator test case that fails when
|
||||
// this is not copied:
|
||||
quantization->quantized_dimension = src_quantization->quantized_dimension();
|
||||
|
||||
result->quantization = {kTfLiteAffineQuantization, quantization};
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus InitializeTfLiteEvalTensorFromFlatbuffer(
|
||||
const tflite::Tensor& flatbuffer_tensor,
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
|
||||
ErrorReporter* error_reporter, TfLiteEvalTensor* result) {
|
||||
*result = {};
|
||||
// Make sure the serialized type is one we know how to deal with, and convert
|
||||
// it from a flatbuffer enum into a constant used by the kernel C API.
|
||||
TF_LITE_ENSURE_STATUS(ConvertTensorType(flatbuffer_tensor.type(),
|
||||
&result->type, error_reporter));
|
||||
|
||||
result->data.data = GetFlatbufferTensorBuffer(flatbuffer_tensor, buffers);
|
||||
|
||||
if (flatbuffer_tensor.shape() == nullptr) {
|
||||
// flatbuffer_tensor.shape() can return a nullptr in the case of a scalar
|
||||
// tensor.
|
||||
result->dims = const_cast<TfLiteIntArray*>(&kZeroLengthIntArray);
|
||||
} else {
|
||||
result->dims = FlatBufferVectorToTfLiteTypeArray(flatbuffer_tensor.shape());
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
size_t MicroAllocator::GetDefaultTailUsage(bool is_memory_planner_given) {
|
||||
// TODO(b/208703041): a template version of AlignSizeUp to make expression
|
||||
// shorter.
|
||||
size_t total_size =
|
||||
AlignSizeUp(sizeof(SingleArenaBufferAllocator),
|
||||
alignof(SingleArenaBufferAllocator)) +
|
||||
AlignSizeUp(sizeof(MicroAllocator), alignof(MicroAllocator)) +
|
||||
AlignSizeUp(sizeof(MicroBuiltinDataAllocator),
|
||||
alignof(MicroBuiltinDataAllocator)) +
|
||||
AlignSizeUp(sizeof(SubgraphAllocations), alignof(SubgraphAllocations));
|
||||
if (!is_memory_planner_given) {
|
||||
total_size +=
|
||||
AlignSizeUp(sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner));
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
MicroAllocator::MicroAllocator(SingleArenaBufferAllocator* memory_allocator,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter)
|
||||
: non_persistent_buffer_allocator_(memory_allocator),
|
||||
persistent_buffer_allocator_(memory_allocator),
|
||||
memory_planner_(memory_planner),
|
||||
error_reporter_(error_reporter),
|
||||
model_is_allocating_(false) {}
|
||||
|
||||
MicroAllocator::MicroAllocator(
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator,
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator,
|
||||
MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter)
|
||||
: non_persistent_buffer_allocator_(non_persistent_buffer_allocator),
|
||||
persistent_buffer_allocator_(persistent_buffer_allocator),
|
||||
memory_planner_(memory_planner),
|
||||
error_reporter_(error_reporter),
|
||||
model_is_allocating_(false) {}
|
||||
|
||||
MicroAllocator::~MicroAllocator() {}
|
||||
|
||||
MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter) {
|
||||
uint8_t* aligned_arena =
|
||||
AlignPointerUp(tensor_arena, MicroArenaBufferAlignment());
|
||||
size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena;
|
||||
SingleArenaBufferAllocator* memory_allocator =
|
||||
SingleArenaBufferAllocator::Create(error_reporter, aligned_arena,
|
||||
aligned_arena_size);
|
||||
|
||||
return Create(memory_allocator, memory_planner, error_reporter);
|
||||
}
|
||||
|
||||
MicroAllocator* MicroAllocator::Create(uint8_t* tensor_arena, size_t arena_size,
|
||||
ErrorReporter* error_reporter) {
|
||||
uint8_t* aligned_arena =
|
||||
AlignPointerUp(tensor_arena, MicroArenaBufferAlignment());
|
||||
size_t aligned_arena_size = tensor_arena + arena_size - aligned_arena;
|
||||
SingleArenaBufferAllocator* memory_allocator =
|
||||
SingleArenaBufferAllocator::Create(error_reporter, aligned_arena,
|
||||
aligned_arena_size);
|
||||
|
||||
// By default create GreedyMemoryPlanner.
|
||||
// If a different MemoryPlanner is needed, use the other api.
|
||||
uint8_t* memory_planner_buffer = memory_allocator->AllocatePersistentBuffer(
|
||||
sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner));
|
||||
GreedyMemoryPlanner* memory_planner =
|
||||
new (memory_planner_buffer) GreedyMemoryPlanner();
|
||||
|
||||
return Create(memory_allocator, memory_planner, error_reporter);
|
||||
}
|
||||
|
||||
MicroAllocator* MicroAllocator::Create(
|
||||
SingleArenaBufferAllocator* memory_allocator,
|
||||
MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter) {
|
||||
TFLITE_DCHECK(memory_allocator != nullptr);
|
||||
TFLITE_DCHECK(error_reporter != nullptr);
|
||||
TFLITE_DCHECK(memory_planner != nullptr);
|
||||
|
||||
uint8_t* allocator_buffer = memory_allocator->AllocatePersistentBuffer(
|
||||
sizeof(MicroAllocator), alignof(MicroAllocator));
|
||||
MicroAllocator* allocator = new (allocator_buffer) MicroAllocator(
|
||||
memory_allocator, memory_allocator, memory_planner, error_reporter);
|
||||
return allocator;
|
||||
}
|
||||
|
||||
MicroAllocator* MicroAllocator::Create(uint8_t* persistent_tensor_arena,
|
||||
size_t persistent_arena_size,
|
||||
uint8_t* non_persistent_tensor_arena,
|
||||
size_t non_persistent_arena_size,
|
||||
ErrorReporter* error_reporter) {
|
||||
TFLITE_DCHECK(persistent_tensor_arena != nullptr);
|
||||
TFLITE_DCHECK(non_persistent_tensor_arena != nullptr);
|
||||
TFLITE_DCHECK(persistent_tensor_arena != non_persistent_tensor_arena);
|
||||
TFLITE_DCHECK(error_reporter != nullptr);
|
||||
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator =
|
||||
CreatePersistentArenaAllocator(persistent_tensor_arena,
|
||||
persistent_arena_size);
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator =
|
||||
CreateNonPersistentArenaAllocator(non_persistent_tensor_arena,
|
||||
non_persistent_arena_size,
|
||||
persistent_buffer_allocator);
|
||||
|
||||
uint8_t* memory_planner_buffer =
|
||||
persistent_buffer_allocator->AllocatePersistentBuffer(
|
||||
sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner));
|
||||
GreedyMemoryPlanner* memory_planner =
|
||||
new (memory_planner_buffer) GreedyMemoryPlanner();
|
||||
|
||||
uint8_t* micro_allocator_buffer =
|
||||
persistent_buffer_allocator->AllocatePersistentBuffer(
|
||||
sizeof(MicroAllocator), alignof(MicroAllocator));
|
||||
MicroAllocator* allocator = new (micro_allocator_buffer) MicroAllocator(
|
||||
persistent_buffer_allocator, non_persistent_buffer_allocator,
|
||||
memory_planner, error_reporter);
|
||||
return allocator;
|
||||
}
|
||||
|
||||
SubgraphAllocations* MicroAllocator::StartModelAllocation(const Model* model) {
|
||||
TFLITE_DCHECK(model != nullptr);
|
||||
|
||||
if (model_is_allocating_) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"MicroAllocator: Model allocation started before "
|
||||
"finishing previously allocated model");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
model_is_allocating_ = true;
|
||||
|
||||
uint8_t* data_allocator_buffer =
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(MicroBuiltinDataAllocator),
|
||||
alignof(MicroBuiltinDataAllocator));
|
||||
builtin_data_allocator_ = new (data_allocator_buffer)
|
||||
MicroBuiltinDataAllocator(persistent_buffer_allocator_);
|
||||
|
||||
if (InitScratchBufferData() != kTfLiteOk) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Allocate struct to store eval tensors, nodes and registrations.
|
||||
SubgraphAllocations* output = reinterpret_cast<SubgraphAllocations*>(
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(SubgraphAllocations) * model->subgraphs()->size(),
|
||||
alignof(SubgraphAllocations)));
|
||||
if (output == nullptr) {
|
||||
MicroPrintf("Failed to allocate memory for model metadata.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (AllocateTfLiteEvalTensors(model, output) != kTfLiteOk ||
|
||||
AllocateNodeAndRegistrations(model, output) != kTfLiteOk) {
|
||||
return nullptr;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::FinishModelAllocation(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations,
|
||||
ScratchBufferHandle** scratch_buffer_handles) {
|
||||
if (!model_is_allocating_) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"MicroAllocator: Model allocation finished before "
|
||||
"starting allocating model");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Allocate scratch buffer metadata.
|
||||
TF_LITE_ENSURE_STATUS(AllocateScratchBufferHandles(
|
||||
scratch_buffer_handles, scratch_buffer_request_count_));
|
||||
|
||||
// Allocate buffers for variable tensors.
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
TF_LITE_ENSURE_STATUS(AllocateVariables(
|
||||
subgraph, subgraph_allocations[subgraph_idx].tensors));
|
||||
}
|
||||
|
||||
// Plan all subgraphs and scratch buffers together.
|
||||
TF_LITE_ENSURE_STATUS(CommitStaticMemoryPlan(model, subgraph_allocations,
|
||||
*scratch_buffer_handles));
|
||||
model_is_allocating_ = false;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
void* MicroAllocator::AllocatePersistentBuffer(size_t bytes) {
|
||||
return persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
bytes, MicroArenaBufferAlignment());
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::RequestScratchBufferInArena(size_t bytes,
|
||||
int subgraph_idx,
|
||||
int* buffer_idx) {
|
||||
// All scratch buffer requests are stored in the head section of the arena
|
||||
// when a model is in the prepare phase. First align a scratch buffer request
|
||||
// pointer to the start of the head:
|
||||
internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
|
||||
|
||||
// Count the number of requested scratch buffers for the current node:
|
||||
size_t current_node_request_count = 0;
|
||||
for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
|
||||
if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
|
||||
++current_node_request_count;
|
||||
}
|
||||
}
|
||||
|
||||
// First, ensure that the per-kernel request has not exceeded the limit:
|
||||
if (current_node_request_count >= kMaxScratchBuffersPerOp) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Scratch buffer request exeeds limit per operator (%d)",
|
||||
kMaxScratchBuffersPerOp);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Initialize and assign values for the request at the current index:
|
||||
internal::ScratchBufferRequest* current_request =
|
||||
&requests[scratch_buffer_request_count_];
|
||||
*current_request = {};
|
||||
// Assign -1 as a sentinel value that will be updated when the node finishes
|
||||
// allocating:
|
||||
current_request->bytes = bytes;
|
||||
current_request->node_idx = kUnassignedScratchBufferRequestIndex;
|
||||
current_request->subgraph_idx = subgraph_idx;
|
||||
|
||||
// Assign the current request index to the out-param:
|
||||
*buffer_idx = scratch_buffer_request_count_;
|
||||
|
||||
// Bump the request count to prepare for the next request:
|
||||
++scratch_buffer_request_count_;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::FinishPrepareNodeAllocations(int node_id) {
|
||||
// When a node has finished preparing, all temp allocations performed by the
|
||||
// kernel should be cleaned up:
|
||||
TF_LITE_ENSURE_STATUS(ResetTempAllocations());
|
||||
|
||||
// Find and update any new scratch buffer requests for the current node:
|
||||
internal::ScratchBufferRequest* requests = GetScratchBufferRequests();
|
||||
|
||||
for (size_t i = 0; i < scratch_buffer_request_count_; ++i) {
|
||||
// A request with a node_idx of -1 is a sentinel value used to indicate this
|
||||
// was a new request for the current node. The allocator finally knows the
|
||||
// node index at this point. Assign the value and update the list of new
|
||||
// requests so the head section can be adjusted to allow for the next kernel
|
||||
// to allocate at most kMaxScratchBuffersPerOp requests:
|
||||
if (requests[i].node_idx == kUnassignedScratchBufferRequestIndex) {
|
||||
requests[i].node_idx = node_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the head is re-adjusted to allow for another at-most
|
||||
// kMaxScratchBuffersPerOp scratch buffer requests in the next operator:
|
||||
TF_LITE_ENSURE_STATUS(non_persistent_buffer_allocator_->ResizeBuffer(
|
||||
scratch_buffer_head_,
|
||||
sizeof(internal::ScratchBufferRequest) *
|
||||
(scratch_buffer_request_count_ + kMaxScratchBuffersPerOp),
|
||||
alignof(internal::ScratchBufferRequest)));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
size_t MicroAllocator::used_bytes() const {
|
||||
return non_persistent_buffer_allocator_->GetNonPersistentUsedBytes() +
|
||||
persistent_buffer_allocator_->GetPersistentUsedBytes();
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::AllocateNodeAndRegistrations(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations) {
|
||||
TFLITE_DCHECK(subgraph_allocations != nullptr);
|
||||
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
|
||||
uint32_t operators_size = NumSubgraphOperators(subgraph);
|
||||
|
||||
// Initialize NodeAndRegistrations for the subgraph.
|
||||
NodeAndRegistration* output = reinterpret_cast<NodeAndRegistration*>(
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(NodeAndRegistration) * operators_size,
|
||||
alignof(NodeAndRegistration)));
|
||||
if (output == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Failed to allocate memory for node_and_registrations.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
subgraph_allocations[subgraph_idx].node_and_registrations = output;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensor(
|
||||
const Model* model, const SubgraphAllocations* subgraph_allocations,
|
||||
int tensor_index, int subgraph_index) {
|
||||
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
|
||||
// This value is allocated from persistent arena space. It is guaranteed to be
|
||||
// around for the lifetime of the application.
|
||||
TfLiteTensor* tensor = AllocatePersistentTfLiteTensorInternal();
|
||||
|
||||
// Populate any fields from the flatbuffer, since this TfLiteTensor struct is
|
||||
// allocated in the persistent section of the arena, ensure that additional
|
||||
// allocations also take place in that section of the arena.
|
||||
if (PopulateTfLiteTensorFromFlatbuffer(
|
||||
model, tensor, tensor_index, subgraph_index,
|
||||
/*allocate_temp=*/false) != kTfLiteOk) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Failed to populate a persistent TfLiteTensor struct "
|
||||
"from flatbuffer data!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (subgraph_allocations != nullptr) {
|
||||
// Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
|
||||
// and not located in the flatbuffer are stored on the pre-allocated list of
|
||||
// TfLiteEvalTensors structs. These structs are the source of truth, simply
|
||||
// point the corresponding buffer to the new TfLiteTensor data value.
|
||||
tensor->data.data =
|
||||
subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
|
||||
// TfLiteEvalTensor structs must also be the source of truth for the
|
||||
// TfLiteTensor dims.
|
||||
tensor->dims =
|
||||
subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
|
||||
}
|
||||
return tensor;
|
||||
}
|
||||
|
||||
void MicroAllocator::DeallocateTempTfLiteTensor(TfLiteTensor* tensor) {
|
||||
TFLITE_DCHECK(tensor != nullptr);
|
||||
|
||||
if (tensor->quantization.type == kTfLiteAffineQuantization) {
|
||||
TFLITE_DCHECK(tensor->quantization.params != nullptr);
|
||||
TfLiteAffineQuantization* quantization =
|
||||
reinterpret_cast<TfLiteAffineQuantization*>(
|
||||
tensor->quantization.params);
|
||||
|
||||
non_persistent_buffer_allocator_->DeallocateTemp(
|
||||
reinterpret_cast<uint8_t*>(quantization->zero_point));
|
||||
non_persistent_buffer_allocator_->DeallocateTemp(
|
||||
reinterpret_cast<uint8_t*>(quantization));
|
||||
}
|
||||
|
||||
// Clear the data in case someone still access tensor arena by mistake
|
||||
tensor->quantization.type = kTfLiteNoQuantization;
|
||||
tensor->quantization.params = nullptr;
|
||||
tensor->data.data = nullptr;
|
||||
tensor->dims = nullptr;
|
||||
non_persistent_buffer_allocator_->DeallocateTemp(
|
||||
reinterpret_cast<uint8_t*>(tensor));
|
||||
}
|
||||
|
||||
TfLiteTensor* MicroAllocator::AllocateTempTfLiteTensor(
|
||||
const Model* model, const SubgraphAllocations* subgraph_allocations,
|
||||
int tensor_index, int subgraph_index) {
|
||||
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_index);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
|
||||
// This value is allocated from temporary arena space. It is guaranteed to be
|
||||
// around for at least the scope of the calling function. Since this struct
|
||||
// allocation takes place in temp space, no need to own or cleanup.
|
||||
TfLiteTensor* tensor = reinterpret_cast<TfLiteTensor*>(
|
||||
non_persistent_buffer_allocator_->AllocateTemp(sizeof(TfLiteTensor),
|
||||
alignof(TfLiteTensor)));
|
||||
|
||||
// Populate any fields from the flatbuffer, since this TfLiteTensor struct is
|
||||
// allocated in the temp section of the arena, ensure that additional
|
||||
// allocations also take place in that section of the arena.
|
||||
if (PopulateTfLiteTensorFromFlatbuffer(model, tensor, tensor_index,
|
||||
subgraph_index,
|
||||
/*allocate_temp=*/true) != kTfLiteOk) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Failed to populate a temp TfLiteTensor struct from flatbuffer data!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (subgraph_allocations != nullptr) {
|
||||
// Tensor buffers that are allocated at runtime (e.g. non-weight buffers)
|
||||
// and not located in the flatbuffer are stored on the pre-allocated list of
|
||||
// TfLiteEvalTensors structs. These structs are the source of truth, simply
|
||||
// point the corresponding buffer to the new TfLiteTensor data value.
|
||||
tensor->data.data =
|
||||
subgraph_allocations[subgraph_index].tensors[tensor_index].data.data;
|
||||
// TfLiteEvalTensor structs must also be the source of truth for the
|
||||
// TfLiteTensor dims.
|
||||
tensor->dims =
|
||||
subgraph_allocations[subgraph_index].tensors[tensor_index].dims;
|
||||
}
|
||||
return tensor;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::ResetTempAllocations() {
|
||||
return non_persistent_buffer_allocator_->ResetTempAllocations();
|
||||
}
|
||||
|
||||
bool MicroAllocator::IsAllTempDeallocated() {
|
||||
return non_persistent_buffer_allocator_->IsAllTempDeallocated();
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::AllocateTfLiteEvalTensors(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations) {
|
||||
TFLITE_DCHECK(subgraph_allocations != nullptr);
|
||||
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
const SubGraph* subgraph = model->subgraphs()->Get(subgraph_idx);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
|
||||
size_t alloc_count = subgraph->tensors()->size();
|
||||
TfLiteEvalTensor* tensors = reinterpret_cast<TfLiteEvalTensor*>(
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(TfLiteEvalTensor) * alloc_count, alignof(TfLiteEvalTensor)));
|
||||
if (tensors == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Failed to allocate memory for context->eval_tensors, "
|
||||
"%d bytes required",
|
||||
sizeof(TfLiteEvalTensor) * alloc_count);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < alloc_count; ++i) {
|
||||
TfLiteStatus status = internal::InitializeTfLiteEvalTensorFromFlatbuffer(
|
||||
*subgraph->tensors()->Get(i), model->buffers(), error_reporter_,
|
||||
&tensors[i]);
|
||||
if (status != kTfLiteOk) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_, "Failed to initialize tensor %d",
|
||||
i);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
subgraph_allocations[subgraph_idx].tensors = tensors;
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::AllocateVariables(const SubGraph* subgraph,
|
||||
TfLiteEvalTensor* eval_tensors) {
|
||||
for (size_t i = 0; i < subgraph->tensors()->size(); ++i) {
|
||||
auto* tensor = subgraph->tensors()->Get(i);
|
||||
if (tensor->is_variable()) {
|
||||
size_t buffer_size;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
TfLiteEvalTensorByteLength(&eval_tensors[i], &buffer_size));
|
||||
|
||||
eval_tensors[i].data.data =
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
buffer_size, MicroArenaBufferAlignment());
|
||||
|
||||
if (eval_tensors[i].data.data == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Failed to allocate variable tensor of size %d",
|
||||
buffer_size);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteTensor* MicroAllocator::AllocatePersistentTfLiteTensorInternal() {
|
||||
return reinterpret_cast<TfLiteTensor*>(
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(TfLiteTensor), alignof(TfLiteTensor)));
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
|
||||
const Model* model, TfLiteTensor* tensor, int tensor_index,
|
||||
int subgraph_idx, bool allocate_temp) {
|
||||
// TODO(b/162311891): This method serves as a stub to ensure quantized
|
||||
// allocations in the tail can be recorded. Once the interpreter has APIs for
|
||||
// accessing buffers on TfLiteEvalTensor this method can be dropped.
|
||||
return internal::InitializeTfLiteTensorFromFlatbuffer(
|
||||
persistent_buffer_allocator_, non_persistent_buffer_allocator_,
|
||||
allocate_temp,
|
||||
*model->subgraphs()->Get(subgraph_idx)->tensors()->Get(tensor_index),
|
||||
model->buffers(), error_reporter_, tensor);
|
||||
}
|
||||
|
||||
ErrorReporter* MicroAllocator::error_reporter() const {
|
||||
return error_reporter_;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::CommitStaticMemoryPlan(
|
||||
const Model* model, SubgraphAllocations* allocations,
|
||||
ScratchBufferHandle* scratch_buffer_handles) {
|
||||
size_t head_usage = 0;
|
||||
// Create static memory plan
|
||||
// 1. Calculate AllocationInfo to know the lifetime of each tensor/buffer.
|
||||
// 2. Add them into the planner (such as the GreedyMemoryPlanner).
|
||||
// 3. Static memory planning using the planner.
|
||||
// 4. Set tensor/buffer pointers based on the offsets from the previous step.
|
||||
//
|
||||
// Note that AllocationInfo is only needed for creating the plan. It will be
|
||||
// allocated from the temp section and cleaned up at the bottom of this
|
||||
// function.
|
||||
|
||||
// Use the AllocationInfoBuilder class to help determine where buffers are
|
||||
// used in the subgraph.
|
||||
AllocationInfoBuilder builder(model, non_persistent_buffer_allocator_,
|
||||
error_reporter_);
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
builder.CreateAllocationInfo(scratch_buffer_request_count_));
|
||||
|
||||
const int32_t* offline_planner_offsets = nullptr;
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
builder.GetOfflinePlannedOffsets(&offline_planner_offsets));
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
builder.InitializeAllocationInfo(offline_planner_offsets, allocations));
|
||||
|
||||
internal::ScratchBufferRequest* scratch_buffer_requests =
|
||||
GetScratchBufferRequests();
|
||||
TF_LITE_ENSURE_STATUS(builder.MarkAllocationLifetimes(
|
||||
0, scratch_buffer_requests, scratch_buffer_handles, allocations));
|
||||
int allocation_info_count = builder.AllocationCount();
|
||||
AllocationInfo* allocation_info = builder.Finish();
|
||||
|
||||
// Remaining arena size that memory planner can use for calculating offsets.
|
||||
size_t remaining_arena_size =
|
||||
non_persistent_buffer_allocator_->GetAvailableMemory(
|
||||
MicroArenaBufferAlignment());
|
||||
uint8_t* planner_arena = non_persistent_buffer_allocator_->AllocateTemp(
|
||||
remaining_arena_size, MicroArenaBufferAlignment());
|
||||
TF_LITE_ENSURE(error_reporter_, planner_arena != nullptr);
|
||||
memory_planner_->Init(planner_arena, remaining_arena_size);
|
||||
TF_LITE_ENSURE_STATUS(CreatePlan(error_reporter_, memory_planner_,
|
||||
allocation_info, allocation_info_count));
|
||||
|
||||
// Commit the plan.
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
CommitPlan(error_reporter_, memory_planner_,
|
||||
non_persistent_buffer_allocator_->GetOverlayMemoryAddress(),
|
||||
allocation_info, allocation_info_count));
|
||||
|
||||
// Reset all temp allocations used above:
|
||||
builder.FreeAllocationInfo();
|
||||
non_persistent_buffer_allocator_->DeallocateTemp(planner_arena);
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
non_persistent_buffer_allocator_->ResetTempAllocations());
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
non_persistent_buffer_allocator_->DeallocateResizableBuffer(
|
||||
scratch_buffer_head_));
|
||||
|
||||
#ifdef TF_LITE_SHOW_MEMORY_USE
|
||||
memory_planner_->PrintMemoryPlan();
|
||||
#endif
|
||||
head_usage = memory_planner_->GetMaximumMemorySize();
|
||||
|
||||
// The head is used to store memory plans for one model at a time during the
|
||||
// model preparation stage, and is re-purposed to store scratch buffer handles
|
||||
// during model invocation. The head must be as large as the greater of the
|
||||
// largest model memory plan's size and the total space required for all
|
||||
// scratch buffer handles.
|
||||
if (max_head_buffer_usage_ < head_usage) {
|
||||
max_head_buffer_usage_ = head_usage;
|
||||
}
|
||||
|
||||
// The head is used for storing scratch buffer allocations before finalizing a
|
||||
// memory plan in this function. Ensure that the head is set to the largest
|
||||
// memory plan sent through the allocator:
|
||||
TF_LITE_ENSURE_STATUS(
|
||||
non_persistent_buffer_allocator_->ReserveNonPersistentOverlayMemory(
|
||||
max_head_buffer_usage_, MicroArenaBufferAlignment()));
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::AllocateScratchBufferHandles(
|
||||
ScratchBufferHandle** scratch_buffer_handles, size_t handle_count) {
|
||||
TFLITE_DCHECK(scratch_buffer_handles != nullptr);
|
||||
|
||||
if (scratch_buffer_request_count_ == 0) {
|
||||
// No scratch buffer requests were requested during model allocation.
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// Allocate a consecutive block of memory store the scratch buffer handles.
|
||||
// This alignment ensures quick lookup during inference time for the model:
|
||||
*scratch_buffer_handles = reinterpret_cast<ScratchBufferHandle*>(
|
||||
persistent_buffer_allocator_->AllocatePersistentBuffer(
|
||||
sizeof(ScratchBufferHandle) * handle_count,
|
||||
alignof(ScratchBufferHandle)));
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroAllocator::InitScratchBufferData() {
|
||||
// A model is preparing to allocate resources, ensure that scratch buffer
|
||||
// request counter is cleared:
|
||||
scratch_buffer_request_count_ = 0;
|
||||
|
||||
// All requests will be stored in the head section. Each kernel is allowed at
|
||||
// most kMaxScratchBuffersPerOp requests. Adjust the head to reserve at most
|
||||
// that many requests to begin:
|
||||
scratch_buffer_head_ =
|
||||
non_persistent_buffer_allocator_->AllocateResizableBuffer(
|
||||
sizeof(internal::ScratchBufferRequest) * kMaxScratchBuffersPerOp,
|
||||
alignof(internal::ScratchBufferRequest));
|
||||
if (scratch_buffer_head_ == nullptr) {
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
internal::ScratchBufferRequest* MicroAllocator::GetScratchBufferRequests() {
|
||||
return reinterpret_cast<internal::ScratchBufferRequest*>(AlignPointerUp(
|
||||
scratch_buffer_head_, alignof(internal::ScratchBufferRequest)));
|
||||
}
|
||||
|
||||
BuiltinDataAllocator* MicroAllocator::GetBuiltinDataAllocator() {
|
||||
return builtin_data_allocator_;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,331 +0,0 @@
|
||||
/* Copyright 2020 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_MICRO_ALLOCATOR_H_
|
||||
#define TENSORFLOW_LITE_MICRO_MICRO_ALLOCATOR_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/core/api/flatbuffer_conversions.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/single_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
#include "tensorflow/lite/micro/flatbuffer_utils.h"
|
||||
#include "tensorflow/lite/micro/memory_planner/micro_memory_planner.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// TODO(b/199402574): rename to tflite_internal or just remove internal
|
||||
// namespace.
|
||||
namespace internal {
|
||||
|
||||
// Sets up all of the data structure members for a TfLiteTensor based on the
|
||||
// contents of a serialized tensor in the flatbuffer.
|
||||
// TODO(b/162311891): Drop this method when the interpreter has an API for
|
||||
// returning buffers on TfLiteEvalTensor.
|
||||
TfLiteStatus InitializeTfLiteTensorFromFlatbuffer(
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator,
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator,
|
||||
bool allocate_temp, const tflite::Tensor& flatbuffer_tensor,
|
||||
const flatbuffers::Vector<flatbuffers::Offset<Buffer>>* buffers,
|
||||
ErrorReporter* error_reporter, TfLiteTensor* result);
|
||||
|
||||
// Holds placeholder information for a scratch buffer request from a kernel.
|
||||
// This struct is only used during the model prepare stage. Each request from a
|
||||
// kernel is stored in the head section. During the prepare stage, the head
|
||||
// section will at least hold kMaxScratchBuffersPerOp number of requests plus
|
||||
// any requests from previous kernel requests.
|
||||
//
|
||||
// When the memory plan is finalized, these structs are no longer used in favor
|
||||
// of a sequential, array of ScratchBufferHandle allocations in the tail
|
||||
// section. These allocations are indexed by the request API defined in the
|
||||
// TfLiteContext struct.
|
||||
struct ScratchBufferRequest {
|
||||
// Number of bytes required by the buffer. The actual allocated size might be
|
||||
// greater than `bytes` due to buffer alignment.
|
||||
size_t bytes;
|
||||
// Node where the buffer is allocated for. This provides useful information to
|
||||
// determine the lifetime of the buffer. In AllocationInfo, this buffer will
|
||||
// have `before` = node_idx and `after` = node_idx.
|
||||
int node_idx;
|
||||
int subgraph_idx;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
struct NodeAndRegistration {
|
||||
TfLiteNode node;
|
||||
const TfLiteRegistration* registration;
|
||||
};
|
||||
|
||||
// Holds a pointer to a buffer for a scratch buffer requested by a kernel during
|
||||
// the model prepare stage. This struct is allocated in-place and allows for
|
||||
// quick pointer-indexed lookup for speed during model inference.
|
||||
struct ScratchBufferHandle {
|
||||
// Pointer to location of the scratch buffer:
|
||||
uint8_t* data;
|
||||
};
|
||||
|
||||
// Stores all per-subgraph allocations. This includes the node and registration
|
||||
// array, and tensor list for each subgraph.
|
||||
struct SubgraphAllocations {
|
||||
NodeAndRegistration* node_and_registrations;
|
||||
TfLiteEvalTensor* tensors;
|
||||
};
|
||||
|
||||
// Allocator responsible for allocating memory for all intermediate tensors
|
||||
// necessary to invoke a model.
|
||||
//
|
||||
// The lifetime of the model, tensor arena and error reporter must be at
|
||||
// least as long as that of the allocator object, since the allocator needs
|
||||
// them to be accessible during its entire lifetime.
|
||||
//
|
||||
// The MicroAllocator simply plans out additional allocations that are required
|
||||
// to standup a model for inference in TF Micro. This class currently relies on
|
||||
// an additional allocator - SingleArenaBufferAllocator - for all allocations
|
||||
// from an arena. These allocations are divided into head (non-persistent) and
|
||||
// tail (persistent) regions:
|
||||
//
|
||||
// Memory layout to help understand how it works
|
||||
// This information could change in the future version.
|
||||
// ************** .memory_allocator->GetBuffer()
|
||||
// Tensors/Scratch buffers (head)
|
||||
// ************** .head_watermark
|
||||
// unused memory
|
||||
// ************** .memory_allocator->GetBuffer() + ->GetMaxBufferSize()
|
||||
// - ->GetDataSize()
|
||||
// persistent area (tail)
|
||||
// ************** .memory_allocator->GetBuffer() + ->GetMaxBufferSize()
|
||||
class MicroAllocator {
|
||||
public:
|
||||
// Creates a MicroAllocator instance from a given tensor arena. This arena
|
||||
// will be managed by the created instance. The GreedyMemoryPlanner will
|
||||
// by default be used and created on the arena.
|
||||
// Note: Please use alignas(16) to make sure tensor_arena is 16
|
||||
// bytes aligned, otherwise some head room will be wasted.
|
||||
// TODO(b/157615197): Cleanup constructor + factory usage.
|
||||
static MicroAllocator* Create(uint8_t* tensor_arena, size_t arena_size,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
// Creates a MicroAllocator instance from a given tensor arena and a given
|
||||
// MemoryPlanner. This arena will be managed by the created instance. Note:
|
||||
// Please use alignas(16) to make sure tensor_arena is 16 bytes
|
||||
// aligned, otherwise some head room will be wasted.
|
||||
static MicroAllocator* Create(uint8_t* tensor_arena, size_t arena_size,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
// Creates a MicroAllocator instance using the provided
|
||||
// SingleArenaBufferAllocator instance and the MemoryPlanner. This allocator
|
||||
// instance will use the SingleArenaBufferAllocator instance to manage
|
||||
// allocations internally.
|
||||
static MicroAllocator* Create(SingleArenaBufferAllocator* memory_allocator,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
// Creates a MicroAllocator instance using the provided
|
||||
// SingleArenaBufferAllocator instance and the MemoryPlanner. This allocator
|
||||
// instance will use the SingleArenaBufferAllocator instance to manage
|
||||
// allocations internally.
|
||||
static MicroAllocator* Create(uint8_t* persistent_tensor_arena,
|
||||
size_t persistent_arena_size,
|
||||
uint8_t* non_persistent_tensor_arena,
|
||||
size_t non_persistent_arena_size,
|
||||
ErrorReporter* error_reporter);
|
||||
|
||||
// Returns the fixed amount of memory overhead of MicroAllocator.
|
||||
static size_t GetDefaultTailUsage(bool is_memory_planner_given);
|
||||
|
||||
// Allocates internal resources required for model inference for each subgraph
|
||||
// from the arena.
|
||||
//
|
||||
// This method will run through the flatbuffer data supplied in the model to
|
||||
// properly allocate tensor, node, and op registration data. This method is
|
||||
// expected to be followed with a call to FinishModelAllocation() Returns a
|
||||
// pointer to an array of SubgraphAllocations (also stored in the tail of the
|
||||
// arena) where each index corresponds to a different subgraph in the model.
|
||||
// Return value is nullptr if the allocations failed.
|
||||
SubgraphAllocations* StartModelAllocation(const Model* model);
|
||||
|
||||
// Finish allocating internal resources required for model inference.
|
||||
//
|
||||
// -Plan the memory for activation tensors and scratch buffers.
|
||||
// -Update eval tensors for each subgraph based on planned offsets.
|
||||
// -Allocate scratch buffer handles array and update based on planned offsets.
|
||||
//
|
||||
// This method should be called after assigning model resources
|
||||
// in StartModelAllocation(). The subgraph_allocations pointer should be the
|
||||
// value passed into this class during StartModelAllocation(). Scratch buffer
|
||||
// handles are stored in the out-param `scratch_buffer_handles` array which is
|
||||
// allocated in this method. This value will be used in `GetScratchBuffer`
|
||||
// call to retrieve scratch buffers.
|
||||
TfLiteStatus FinishModelAllocation(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations,
|
||||
ScratchBufferHandle** scratch_buffer_handles);
|
||||
|
||||
// Allocates a TfLiteTensor struct and populates the returned value with
|
||||
// properties from the model flatbuffer. This struct is allocated from
|
||||
// persistent arena memory is only guaranteed for the lifetime of the
|
||||
// application. The eval_tensors pointer should be the value passed into this
|
||||
// class during StartModelAllocation() and contains the source-of-truth for
|
||||
// buffers.
|
||||
virtual TfLiteTensor* AllocatePersistentTfLiteTensor(
|
||||
const Model* model, const SubgraphAllocations* subgraph_allocations,
|
||||
int tensor_index, int subgraph_index);
|
||||
|
||||
// Allocates a TfLiteTensor struct and populates the returned value with
|
||||
// properties from the model flatbuffer. This struct is allocated from
|
||||
// temporary arena memory is only guaranteed until a call is made to
|
||||
// ResetTempAllocations(). Subgraph_allocaitons contains the array of
|
||||
// TfLiteEvalTensors. If the newly allocated temp at the specified subgraph
|
||||
// and tensor index is already present int the TfLiteEvalTensor array, its
|
||||
// data buffer will be re-used.
|
||||
virtual TfLiteTensor* AllocateTempTfLiteTensor(
|
||||
const Model* model, const SubgraphAllocations* subgraph_allocations,
|
||||
int tensor_index, int subgraph_index);
|
||||
|
||||
virtual void DeallocateTempTfLiteTensor(TfLiteTensor*);
|
||||
|
||||
// Resets all temporary allocations. This method should be called after a
|
||||
// chain of temp allocations (e.g. chain of TfLiteTensor objects via
|
||||
// AllocateTfLiteTensor()).
|
||||
virtual TfLiteStatus ResetTempAllocations();
|
||||
|
||||
// Returns true if all temporary buffers including temp TfLiteTensor are
|
||||
// already deallocated.
|
||||
virtual bool IsAllTempDeallocated();
|
||||
|
||||
// Allocates persistent buffer which has the same life time as the allocator.
|
||||
// The memory is immediately available and is allocated from the tail of the
|
||||
// arena.
|
||||
virtual void* AllocatePersistentBuffer(size_t bytes);
|
||||
|
||||
// Register a scratch buffer of size `bytes` for Node with `node_id`.
|
||||
// This method only requests a buffer with a given size to be used after a
|
||||
// model has finished allocation via FinishModelAllocation(). All requested
|
||||
// buffers will be accessible by the out-param in that method.
|
||||
TfLiteStatus RequestScratchBufferInArena(size_t bytes, int subgraph_idx,
|
||||
int* buffer_idx);
|
||||
|
||||
// Finish allocating a specific NodeAndRegistration prepare block (kernel
|
||||
// entry for a model) with a given node ID. This call ensures that any scratch
|
||||
// buffer requests and temporary allocations are handled and ready for the
|
||||
// next node prepare block.
|
||||
TfLiteStatus FinishPrepareNodeAllocations(int node_id);
|
||||
|
||||
// Returns the arena usage in bytes, only available after
|
||||
// `FinishModelAllocation`. Otherwise, it will return 0.
|
||||
size_t used_bytes() const;
|
||||
|
||||
BuiltinDataAllocator* GetBuiltinDataAllocator();
|
||||
|
||||
protected:
|
||||
MicroAllocator(SingleArenaBufferAllocator* memory_allocator,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter);
|
||||
MicroAllocator(IPersistentBufferAllocator* persistent_buffer_allocator,
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator,
|
||||
MicroMemoryPlanner* memory_planner,
|
||||
ErrorReporter* error_reporter);
|
||||
virtual ~MicroAllocator();
|
||||
|
||||
// Allocates an array in the arena to hold pointers to the node and
|
||||
// registration pointers required to represent the inference graph of the
|
||||
// model.
|
||||
virtual TfLiteStatus AllocateNodeAndRegistrations(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations);
|
||||
|
||||
// Allocates the list of persistent TfLiteEvalTensors that are used for the
|
||||
// "eval" phase of model inference. These structs will be the source of truth
|
||||
// for all tensor buffers.
|
||||
virtual TfLiteStatus AllocateTfLiteEvalTensors(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations);
|
||||
// Allocates persistent tensor buffers for variable tensors in the subgraph.
|
||||
virtual TfLiteStatus AllocateVariables(const SubGraph* subgraph,
|
||||
TfLiteEvalTensor* eval_tensors);
|
||||
|
||||
// Allocate and return a persistent TfLiteTensor.
|
||||
// TODO(b/162311891): Drop this method when the interpreter has an API for
|
||||
// accessing TfLiteEvalTensor structs.
|
||||
virtual TfLiteTensor* AllocatePersistentTfLiteTensorInternal();
|
||||
|
||||
// Populates a TfLiteTensor struct with data from the model flatbuffer. Any
|
||||
// quantization data is allocated from either the tail (persistent) or temp
|
||||
// sections of the arena based on the allocation flag.
|
||||
virtual TfLiteStatus PopulateTfLiteTensorFromFlatbuffer(const Model* model,
|
||||
TfLiteTensor* tensor,
|
||||
int tensor_index,
|
||||
int subgraph_idx,
|
||||
bool allocate_temp);
|
||||
|
||||
ErrorReporter* error_reporter() const;
|
||||
|
||||
private:
|
||||
// Commits a memory plan for all non-persistent buffer allocations in the
|
||||
// 'head' section of the memory arena. The eval_tensors pointer is the list of
|
||||
// pre-allocated TfLiteEvalTensor structs that will point to the buffers that
|
||||
// will be allocated into the head section in this function call. The
|
||||
// scratch_buffer_handles pointer is the array of pre-allocated
|
||||
// ScratchBufferHandle structs that will point to allocated buffers also in
|
||||
// the head section.
|
||||
virtual TfLiteStatus CommitStaticMemoryPlan(
|
||||
const Model* model, SubgraphAllocations* allocations,
|
||||
ScratchBufferHandle* scratch_buffer_handles);
|
||||
|
||||
// Allocates an array of ScratchBufferHandle structs in the tail section for a
|
||||
// given number of handles.
|
||||
virtual TfLiteStatus AllocateScratchBufferHandles(
|
||||
ScratchBufferHandle** scratch_buffer_handles, size_t handle_count);
|
||||
|
||||
// Clears all internal scratch buffer request counts and resets the head to
|
||||
// prepare for kernels to request scratch buffer data when a model is
|
||||
// preparing.
|
||||
TfLiteStatus InitScratchBufferData();
|
||||
|
||||
// Returns the pointer for the array of ScratchBufferRequest allocations in
|
||||
// the head section.
|
||||
internal::ScratchBufferRequest* GetScratchBufferRequests();
|
||||
|
||||
// A simple memory allocator that always allocate from the arena tail or head.
|
||||
INonPersistentBufferAllocator* non_persistent_buffer_allocator_;
|
||||
IPersistentBufferAllocator* persistent_buffer_allocator_;
|
||||
|
||||
// Allocator used to allocate persistent builtin data.
|
||||
BuiltinDataAllocator* builtin_data_allocator_;
|
||||
|
||||
// Activation buffer memory planner.
|
||||
MicroMemoryPlanner* memory_planner_;
|
||||
|
||||
ErrorReporter* error_reporter_;
|
||||
bool model_is_allocating_;
|
||||
|
||||
// Holds the number of ScratchBufferRequest instances stored in the head
|
||||
// section when a model is allocating.
|
||||
size_t scratch_buffer_request_count_ = 0;
|
||||
|
||||
// Holds ScratchBufferRequest when a model is allocating
|
||||
uint8_t* scratch_buffer_head_ = nullptr;
|
||||
|
||||
// Holds the byte length of the memory plan with the largest head usage. Used
|
||||
// to ensure that multi-tenant allocations can share the head for buffers.
|
||||
size_t max_head_buffer_usage_ = 0;
|
||||
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
#endif // TENSORFLOW_LITE_MICRO_MICRO_ALLOCATOR_H_
|
||||
@@ -1,340 +0,0 @@
|
||||
/* Copyright 2020 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/micro/micro_interpreter.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "flatbuffers/flatbuffers.h" // from @flatbuffers
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/core/api/tensor_utils.h"
|
||||
#include "tensorflow/lite/micro/flatbuffer_utils.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/micro_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_op_resolver.h"
|
||||
#include "tensorflow/lite/micro/micro_profiler.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/schema/schema_utils.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
MicroInterpreter::MicroInterpreter(const Model* model,
|
||||
const MicroOpResolver& op_resolver,
|
||||
uint8_t* tensor_arena,
|
||||
size_t tensor_arena_size,
|
||||
ErrorReporter* error_reporter,
|
||||
MicroResourceVariables* resource_variables,
|
||||
MicroProfiler* profiler)
|
||||
: model_(model),
|
||||
op_resolver_(op_resolver),
|
||||
error_reporter_(error_reporter),
|
||||
allocator_(*MicroAllocator::Create(tensor_arena, tensor_arena_size,
|
||||
error_reporter)),
|
||||
|
||||
graph_(&context_, model, &allocator_, resource_variables),
|
||||
tensors_allocated_(false),
|
||||
initialization_status_(kTfLiteError),
|
||||
input_tensors_(nullptr),
|
||||
output_tensors_(nullptr),
|
||||
micro_context_(&allocator_, model_, &graph_) {
|
||||
Init(profiler);
|
||||
}
|
||||
|
||||
MicroInterpreter::MicroInterpreter(const Model* model,
|
||||
const MicroOpResolver& op_resolver,
|
||||
MicroAllocator* allocator,
|
||||
ErrorReporter* error_reporter,
|
||||
MicroResourceVariables* resource_variables,
|
||||
MicroProfiler* profiler)
|
||||
: model_(model),
|
||||
op_resolver_(op_resolver),
|
||||
error_reporter_(error_reporter),
|
||||
allocator_(*allocator),
|
||||
graph_(&context_, model, allocator, resource_variables),
|
||||
tensors_allocated_(false),
|
||||
initialization_status_(kTfLiteError),
|
||||
input_tensors_(nullptr),
|
||||
output_tensors_(nullptr),
|
||||
micro_context_(&allocator_, model_, &graph_) {
|
||||
Init(profiler);
|
||||
}
|
||||
|
||||
MicroInterpreter::~MicroInterpreter() {
|
||||
if (graph_.GetAllocations() != nullptr) {
|
||||
graph_.FreeSubgraphs();
|
||||
}
|
||||
}
|
||||
|
||||
void MicroInterpreter::Init(MicroProfiler* profiler) {
|
||||
context_.impl_ = static_cast<void*>(µ_context_);
|
||||
context_.ReportError = MicroContextReportOpError;
|
||||
context_.GetTensor = MicroContextGetTensor;
|
||||
context_.GetEvalTensor = MicroContextGetEvalTensor;
|
||||
context_.profiler = profiler;
|
||||
|
||||
initialization_status_ = kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroInterpreter::PrepareNodeAndRegistrationDataFromFlatbuffer() {
|
||||
for (int subgraph_idx = 0; subgraph_idx < graph_.NumSubgraphs();
|
||||
subgraph_idx++) {
|
||||
const SubGraph* subgraph = model_->subgraphs()->Get(subgraph_idx);
|
||||
TFLITE_DCHECK(subgraph != nullptr);
|
||||
|
||||
auto* opcodes = model_->operator_codes();
|
||||
BuiltinDataAllocator* builtin_data_allocator =
|
||||
allocator_.GetBuiltinDataAllocator();
|
||||
uint32_t operators_size = NumSubgraphOperators(subgraph);
|
||||
for (size_t i = 0; i < operators_size; ++i) {
|
||||
const auto* op = subgraph->operators()->Get(i);
|
||||
const size_t index = op->opcode_index();
|
||||
if (index >= opcodes->size()) {
|
||||
MicroPrintf("Missing registration for opcode_index %d\n", index);
|
||||
return kTfLiteError;
|
||||
}
|
||||
const auto* opcode = opcodes->Get(index);
|
||||
TfLiteStatus status =
|
||||
GetRegistrationFromOpCode(opcode, op_resolver_, error_reporter_,
|
||||
&(graph_.GetAllocations()[subgraph_idx]
|
||||
.node_and_registrations[i]
|
||||
.registration));
|
||||
if (status != kTfLiteOk) {
|
||||
MicroPrintf("Failed to get registration from op code %s\n ",
|
||||
EnumNameBuiltinOperator(GetBuiltinCode(opcode)));
|
||||
return status;
|
||||
}
|
||||
const auto* registration = graph_.GetAllocations()[subgraph_idx]
|
||||
.node_and_registrations[i]
|
||||
.registration;
|
||||
if (registration == nullptr) {
|
||||
MicroPrintf("Skipping op for opcode_index %d\n", index);
|
||||
return kTfLiteError;
|
||||
}
|
||||
BuiltinOperator op_type =
|
||||
static_cast<BuiltinOperator>(registration->builtin_code);
|
||||
|
||||
const char* custom_data = nullptr;
|
||||
size_t custom_data_size = 0;
|
||||
unsigned char* builtin_data = nullptr;
|
||||
|
||||
if (op_type == BuiltinOperator_CUSTOM) {
|
||||
// Custom Ops may or may not have a non-null custom_options field.
|
||||
if (op->custom_options() != nullptr) {
|
||||
custom_data =
|
||||
reinterpret_cast<const char*>(op->custom_options()->data());
|
||||
custom_data_size = op->custom_options()->size();
|
||||
}
|
||||
} else {
|
||||
if (op->custom_options() != nullptr) {
|
||||
MicroPrintf(
|
||||
"Unsupported behavior: found builtin operator %s with custom "
|
||||
"options.\n",
|
||||
EnumNameBuiltinOperator(op_type));
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
MicroOpResolver::BuiltinParseFunction parser =
|
||||
op_resolver_.GetOpDataParser(op_type);
|
||||
if (parser == nullptr) {
|
||||
MicroPrintf("Did not find a parser for %s",
|
||||
EnumNameBuiltinOperator(op_type));
|
||||
|
||||
return kTfLiteError;
|
||||
}
|
||||
TF_LITE_ENSURE_STATUS(parser(op, error_reporter_,
|
||||
builtin_data_allocator,
|
||||
(void**)(&builtin_data)));
|
||||
}
|
||||
|
||||
TfLiteIntArray* inputs_array =
|
||||
FlatBufferVectorToTfLiteTypeArray(op->inputs());
|
||||
TfLiteIntArray* outputs_array =
|
||||
FlatBufferVectorToTfLiteTypeArray(op->outputs());
|
||||
|
||||
TfLiteNode* node = &(
|
||||
graph_.GetAllocations()[subgraph_idx].node_and_registrations[i].node);
|
||||
*node = {};
|
||||
node->inputs = inputs_array;
|
||||
node->outputs = outputs_array;
|
||||
node->builtin_data = reinterpret_cast<void*>(builtin_data);
|
||||
node->custom_initial_data = custom_data;
|
||||
node->custom_initial_data_size = custom_data_size;
|
||||
|
||||
if (op->intermediates() && (op->intermediates()->size() > 0)) {
|
||||
node->intermediates =
|
||||
FlatBufferVectorToTfLiteTypeArray(op->intermediates());
|
||||
}
|
||||
}
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroInterpreter::AllocateTensors() {
|
||||
SubgraphAllocations* allocations = allocator_.StartModelAllocation(model_);
|
||||
|
||||
if (allocations == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Failed starting model allocation.\n");
|
||||
initialization_status_ = kTfLiteError;
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
graph_.SetSubgraphAllocations(allocations);
|
||||
|
||||
TF_LITE_ENSURE_STATUS(PrepareNodeAndRegistrationDataFromFlatbuffer());
|
||||
|
||||
// Only allow AllocatePersistentBuffer in Init stage.
|
||||
context_.AllocatePersistentBuffer = MicroContextAllocatePersistentBuffer;
|
||||
context_.RequestScratchBufferInArena = nullptr;
|
||||
context_.GetScratchBuffer = nullptr;
|
||||
context_.GetExternalContext = nullptr;
|
||||
TF_LITE_ENSURE_STATUS(graph_.InitSubgraphs());
|
||||
|
||||
// Both AllocatePersistentBuffer and RequestScratchBufferInArena is
|
||||
// available in Prepare stage.
|
||||
context_.RequestScratchBufferInArena =
|
||||
MicroContextRequestScratchBufferInArena;
|
||||
// external_context become available in Prepare stage.
|
||||
context_.GetExternalContext = MicroContextGetExternalContext;
|
||||
|
||||
TF_LITE_ENSURE_STATUS(graph_.PrepareSubgraphs());
|
||||
|
||||
// Prepare is done, we're ready for Invoke. Memory allocation is no longer
|
||||
// allowed. Kernels can only fetch scratch buffers via GetScratchBuffer.
|
||||
context_.AllocatePersistentBuffer = nullptr;
|
||||
context_.RequestScratchBufferInArena = nullptr;
|
||||
context_.GetScratchBuffer = MicroContextGetScratchBuffer;
|
||||
|
||||
TF_LITE_ENSURE_OK(&context_, allocator_.FinishModelAllocation(
|
||||
model_, graph_.GetAllocations(),
|
||||
&scratch_buffer_handles_));
|
||||
|
||||
micro_context_.SetScratchBufferHandles(scratch_buffer_handles_);
|
||||
|
||||
// TODO(b/162311891): Drop these allocations when the interpreter supports
|
||||
// handling buffers from TfLiteEvalTensor.
|
||||
input_tensors_ =
|
||||
reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer(
|
||||
sizeof(TfLiteTensor*) * inputs_size()));
|
||||
if (input_tensors_ == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Failed to allocate memory for context->input_tensors_, "
|
||||
"%d bytes required",
|
||||
sizeof(TfLiteTensor*) * inputs_size());
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < inputs_size(); ++i) {
|
||||
input_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor(
|
||||
model_, graph_.GetAllocations(), inputs().Get(i), 0);
|
||||
if (input_tensors_[i] == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Failed to initialize input tensor %d", i);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/162311891): Drop these allocations when the interpreter supports
|
||||
// handling buffers from TfLiteEvalTensor.
|
||||
output_tensors_ =
|
||||
reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer(
|
||||
sizeof(TfLiteTensor*) * outputs_size()));
|
||||
if (output_tensors_ == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter_,
|
||||
"Failed to allocate memory for context->output_tensors_, "
|
||||
"%d bytes required",
|
||||
sizeof(TfLiteTensor*) * outputs_size());
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < outputs_size(); ++i) {
|
||||
output_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor(
|
||||
model_, graph_.GetAllocations(), outputs().Get(i), 0);
|
||||
if (output_tensors_[i] == nullptr) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Failed to initialize output tensor %d", i);
|
||||
return kTfLiteError;
|
||||
}
|
||||
}
|
||||
|
||||
TF_LITE_ENSURE_STATUS(ResetVariableTensors());
|
||||
|
||||
tensors_allocated_ = true;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus MicroInterpreter::Invoke() {
|
||||
if (initialization_status_ != kTfLiteOk) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Invoke() called after initialization failed\n");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
// Ensure tensors are allocated before the interpreter is invoked to avoid
|
||||
// difficult to debug segfaults.
|
||||
if (!tensors_allocated_) {
|
||||
TF_LITE_ENSURE_OK(&context_, AllocateTensors());
|
||||
}
|
||||
return graph_.InvokeSubgraph(0);
|
||||
}
|
||||
|
||||
TfLiteTensor* MicroInterpreter::input(size_t index) {
|
||||
const size_t length = inputs_size();
|
||||
if (index >= length) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Input index %d out of range (length is %d)", index,
|
||||
length);
|
||||
return nullptr;
|
||||
}
|
||||
return input_tensors_[index];
|
||||
}
|
||||
|
||||
TfLiteTensor* MicroInterpreter::output(size_t index) {
|
||||
const size_t length = outputs_size();
|
||||
if (index >= length) {
|
||||
TF_LITE_REPORT_ERROR(error_reporter_,
|
||||
"Output index %d out of range (length is %d)", index,
|
||||
length);
|
||||
return nullptr;
|
||||
}
|
||||
return output_tensors_[index];
|
||||
}
|
||||
// Repurposing free subgraphs to reset state for some ops for now
|
||||
// will reset api is made. See b/220940833#comment25 for more context.
|
||||
TfLiteStatus MicroInterpreter::Reset() {
|
||||
TfLiteStatus status = graph_.FreeSubgraphs();
|
||||
if (status != kTfLiteOk) {
|
||||
return status;
|
||||
}
|
||||
return graph_.ResetVariableTensors();
|
||||
}
|
||||
|
||||
// TODO: remove this API completely in favor of MicroInterpreter::Reset
|
||||
TfLiteStatus MicroInterpreter::ResetVariableTensors() {
|
||||
return graph_.ResetVariableTensors();
|
||||
}
|
||||
|
||||
TfLiteStatus MicroInterpreter::SetMicroExternalContext(
|
||||
void* external_context_payload) {
|
||||
return micro_context_.set_external_context(external_context_payload);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
@@ -1,177 +0,0 @@
|
||||
/* Copyright 2020 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_MICRO_INTERPRETER_H_
|
||||
#define TENSORFLOW_LITE_MICRO_MICRO_INTERPRETER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "flatbuffers/flatbuffers.h" // from @flatbuffers
|
||||
#include "tensorflow/lite/c/c_api_types.h"
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
|
||||
#include "tensorflow/lite/micro/micro_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_context.h"
|
||||
#include "tensorflow/lite/micro/micro_graph.h"
|
||||
#include "tensorflow/lite/micro/micro_op_resolver.h"
|
||||
#include "tensorflow/lite/micro/micro_profiler.h"
|
||||
#include "tensorflow/lite/portable_type_to_tflitetype.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
/// Copied from tensorflow/lite/version.h to avoid a dependency chain into
|
||||
// tensorflow/core.
|
||||
#define TFLITE_SCHEMA_VERSION (3)
|
||||
|
||||
namespace tflite {
|
||||
|
||||
class MicroInterpreter {
|
||||
public:
|
||||
// The lifetime of the model, op resolver, tensor arena, error reporter,
|
||||
// resource variables, and profiler must be at least as long as that of the
|
||||
// interpreter object, since the interpreter may need to access them at any
|
||||
// time. This means that you should usually create them with the same scope as
|
||||
// each other, for example having them all allocated on the stack as local
|
||||
// variables through a top-level function. The interpreter doesn't do any
|
||||
// deallocation of any of the pointed-to objects, ownership remains with the
|
||||
// caller.
|
||||
MicroInterpreter(const Model* model, const MicroOpResolver& op_resolver,
|
||||
uint8_t* tensor_arena, size_t tensor_arena_size,
|
||||
ErrorReporter* error_reporter,
|
||||
MicroResourceVariables* resource_variables = nullptr,
|
||||
MicroProfiler* profiler = nullptr);
|
||||
|
||||
// Create an interpreter instance using an existing MicroAllocator instance.
|
||||
// This constructor should be used when creating an allocator that needs to
|
||||
// have allocation handled in more than one interpreter or for recording
|
||||
// allocations inside the interpreter. The lifetime of the allocator must be
|
||||
// as long as that of the interpreter object.
|
||||
MicroInterpreter(const Model* model, const MicroOpResolver& op_resolver,
|
||||
MicroAllocator* allocator, ErrorReporter* error_reporter,
|
||||
MicroResourceVariables* resource_variables = nullptr,
|
||||
MicroProfiler* profiler = nullptr);
|
||||
|
||||
~MicroInterpreter();
|
||||
|
||||
// Runs through the model and allocates all necessary input, output and
|
||||
// intermediate tensors.
|
||||
TfLiteStatus AllocateTensors();
|
||||
|
||||
// In order to support partial graph runs for strided models, this can return
|
||||
// values other than kTfLiteOk and kTfLiteError.
|
||||
// TODO(b/149795762): Add this to the TfLiteStatus enum.
|
||||
TfLiteStatus Invoke();
|
||||
|
||||
// This is the recommended API for an application to pass an external payload
|
||||
// pointer as an external context to kernels. The life time of the payload
|
||||
// pointer should be at least as long as this interpreter. TFLM supports only
|
||||
// one external context.
|
||||
TfLiteStatus SetMicroExternalContext(void* external_context_payload);
|
||||
|
||||
TfLiteTensor* input(size_t index);
|
||||
size_t inputs_size() const {
|
||||
return model_->subgraphs()->Get(0)->inputs()->size();
|
||||
}
|
||||
const flatbuffers::Vector<int32_t>& inputs() const {
|
||||
return *model_->subgraphs()->Get(0)->inputs();
|
||||
}
|
||||
TfLiteTensor* input_tensor(size_t index) { return input(index); }
|
||||
template <class T>
|
||||
T* typed_input_tensor(int tensor_index) {
|
||||
if (TfLiteTensor* tensor_ptr = input_tensor(tensor_index)) {
|
||||
if (tensor_ptr->type == typeToTfLiteType<T>()) {
|
||||
return GetTensorData<T>(tensor_ptr);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TfLiteTensor* output(size_t index);
|
||||
size_t outputs_size() const {
|
||||
return model_->subgraphs()->Get(0)->outputs()->size();
|
||||
}
|
||||
const flatbuffers::Vector<int32_t>& outputs() const {
|
||||
return *model_->subgraphs()->Get(0)->outputs();
|
||||
}
|
||||
TfLiteTensor* output_tensor(size_t index) { return output(index); }
|
||||
template <class T>
|
||||
T* typed_output_tensor(int tensor_index) {
|
||||
if (TfLiteTensor* tensor_ptr = output_tensor(tensor_index)) {
|
||||
if (tensor_ptr->type == typeToTfLiteType<T>()) {
|
||||
return GetTensorData<T>(tensor_ptr);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Reset the state to be what you would expect when the interpreter is first
|
||||
// created. i.e. after Init and Prepare is called for the very first time.
|
||||
TfLiteStatus Reset();
|
||||
|
||||
// TODO(b/244457206): remove this in favor of Reset()
|
||||
// Reset all variable tensors to the default value.
|
||||
TfLiteStatus ResetVariableTensors();
|
||||
|
||||
TfLiteStatus initialization_status() const { return initialization_status_; }
|
||||
|
||||
// Populates node and registration pointers representing the inference graph
|
||||
// of the model from values inside the flatbuffer (loaded from the TfLiteModel
|
||||
// instance). Persistent data (e.g. operator data) is allocated from the
|
||||
// arena.
|
||||
TfLiteStatus PrepareNodeAndRegistrationDataFromFlatbuffer();
|
||||
|
||||
// For debugging only.
|
||||
// Returns the actual used arena in bytes. This method gives the optimal arena
|
||||
// size. It's only available after `AllocateTensors` has been called.
|
||||
// Note that normally `tensor_arena` requires 16 bytes alignment to fully
|
||||
// utilize the space. If it's not the case, the optimial arena size would be
|
||||
// arena_used_bytes() + 16.
|
||||
size_t arena_used_bytes() const { return allocator_.used_bytes(); }
|
||||
|
||||
protected:
|
||||
const MicroAllocator& allocator() const { return allocator_; }
|
||||
const TfLiteContext& context() const { return context_; }
|
||||
|
||||
private:
|
||||
// TODO(b/158263161): Consider switching to Create() function to enable better
|
||||
// error reporting during initialization.
|
||||
void Init(MicroProfiler* profiler);
|
||||
|
||||
// Gets the current subgraph index used from within context methods.
|
||||
int get_subgraph_index() { return graph_.GetCurrentSubgraphIndex(); }
|
||||
|
||||
const Model* model_;
|
||||
const MicroOpResolver& op_resolver_;
|
||||
ErrorReporter* error_reporter_;
|
||||
TfLiteContext context_ = {};
|
||||
MicroAllocator& allocator_;
|
||||
MicroGraph graph_;
|
||||
bool tensors_allocated_;
|
||||
|
||||
TfLiteStatus initialization_status_;
|
||||
|
||||
ScratchBufferHandle* scratch_buffer_handles_ = nullptr;
|
||||
|
||||
// TODO(b/162311891): Clean these pointers up when this class supports buffers
|
||||
// from TfLiteEvalTensor.
|
||||
TfLiteTensor** input_tensors_;
|
||||
TfLiteTensor** output_tensors_;
|
||||
|
||||
MicroContext micro_context_;
|
||||
};
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_MICRO_INTERPRETER_H_
|
||||
@@ -1,645 +0,0 @@
|
||||
/* Copyright 2022 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.
|
||||
==============================================================================*/
|
||||
#ifndef TENSORFLOW_LITE_MICRO_MICRO_MUTABLE_OP_RESOLVER_H_
|
||||
#define TENSORFLOW_LITE_MICRO_MICRO_MUTABLE_OP_RESOLVER_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "tensorflow/lite/c/common.h"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/core/api/flatbuffer_conversions.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/op_macros.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
#include "tensorflow/lite/micro/kernels/add.h"
|
||||
#include "tensorflow/lite/micro/kernels/conv.h"
|
||||
#include "tensorflow/lite/micro/kernels/depthwise_conv.h"
|
||||
#include "tensorflow/lite/micro/kernels/ethosu.h"
|
||||
#include "tensorflow/lite/micro/kernels/fully_connected.h"
|
||||
#include "tensorflow/lite/micro/kernels/micro_ops.h"
|
||||
#include "tensorflow/lite/micro/kernels/pooling.h"
|
||||
#include "tensorflow/lite/micro/kernels/reduce.h"
|
||||
#include "tensorflow/lite/micro/kernels/softmax.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_op_resolver.h"
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
|
||||
namespace tflite {
|
||||
TfLiteRegistration* Register_DETECTION_POSTPROCESS();
|
||||
|
||||
template <unsigned int tOpCount>
|
||||
class MicroMutableOpResolver : public MicroOpResolver {
|
||||
public:
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE
|
||||
|
||||
explicit MicroMutableOpResolver(ErrorReporter* error_reporter = nullptr)
|
||||
: error_reporter_(error_reporter) {}
|
||||
|
||||
const TfLiteRegistration* FindOp(tflite::BuiltinOperator op) const override {
|
||||
if (op == BuiltinOperator_CUSTOM) return nullptr;
|
||||
|
||||
for (unsigned int i = 0; i < registrations_len_; ++i) {
|
||||
const TfLiteRegistration& registration = registrations_[i];
|
||||
if (registration.builtin_code == op) {
|
||||
return ®istration;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const TfLiteRegistration* FindOp(const char* op) const override {
|
||||
for (unsigned int i = 0; i < registrations_len_; ++i) {
|
||||
const TfLiteRegistration& registration = registrations_[i];
|
||||
if ((registration.builtin_code == BuiltinOperator_CUSTOM) &&
|
||||
(strcmp(registration.custom_name, op) == 0)) {
|
||||
return ®istration;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MicroOpResolver::BuiltinParseFunction GetOpDataParser(
|
||||
BuiltinOperator op) const override {
|
||||
TFLITE_DCHECK(num_buitin_ops_ <= tOpCount);
|
||||
for (unsigned int i = 0; i < num_buitin_ops_; ++i) {
|
||||
if (builtin_codes_[i] == op) return builtin_parsers_[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Registers a Custom Operator with the MicroOpResolver.
|
||||
//
|
||||
// Only the first call for a given name will be successful. i.e. if this
|
||||
// function is called again for a previously added Custom Operator, the
|
||||
// MicroOpResolver will be unchanged and this function will return
|
||||
// kTfLiteError.
|
||||
TfLiteStatus AddCustom(const char* name, TfLiteRegistration* registration) {
|
||||
if (registrations_len_ >= tOpCount) {
|
||||
MicroPrintf(
|
||||
"Couldn't register custom op '%s', resolver size is too"
|
||||
"small (%d)",
|
||||
name, tOpCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (FindOp(name) != nullptr) {
|
||||
MicroPrintf("Calling AddCustom for the same op more than once ");
|
||||
MicroPrintf("is not supported (Op: %s).", name);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
TfLiteRegistration* new_registration = ®istrations_[registrations_len_];
|
||||
registrations_len_ += 1;
|
||||
|
||||
*new_registration = *registration;
|
||||
new_registration->builtin_code = BuiltinOperator_CUSTOM;
|
||||
new_registration->custom_name = name;
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
// The Add* functions below add the various Builtin operators to the
|
||||
// MicroMutableOpResolver object.
|
||||
|
||||
TfLiteStatus AddAbs() {
|
||||
return AddBuiltin(BuiltinOperator_ABS, tflite::ops::micro::Register_ABS(),
|
||||
ParseAbs);
|
||||
}
|
||||
|
||||
TfLiteStatus AddAdd(const TfLiteRegistration& registration = Register_ADD()) {
|
||||
return AddBuiltin(BuiltinOperator_ADD, registration, ParseAdd);
|
||||
}
|
||||
|
||||
TfLiteStatus AddAddN() {
|
||||
return AddBuiltin(BuiltinOperator_ADD_N, tflite::Register_ADD_N(),
|
||||
ParseAddN);
|
||||
}
|
||||
|
||||
TfLiteStatus AddArgMax() {
|
||||
return AddBuiltin(BuiltinOperator_ARG_MAX,
|
||||
tflite::ops::micro::Register_ARG_MAX(), ParseArgMax);
|
||||
}
|
||||
|
||||
TfLiteStatus AddArgMin() {
|
||||
return AddBuiltin(BuiltinOperator_ARG_MIN,
|
||||
tflite::ops::micro::Register_ARG_MIN(), ParseArgMin);
|
||||
}
|
||||
|
||||
TfLiteStatus AddAssignVariable() {
|
||||
return AddBuiltin(BuiltinOperator_ASSIGN_VARIABLE,
|
||||
tflite::Register_ASSIGN_VARIABLE(), ParseAssignVariable);
|
||||
}
|
||||
|
||||
TfLiteStatus AddAveragePool2D(
|
||||
const TfLiteRegistration& registration = Register_AVERAGE_POOL_2D()) {
|
||||
return AddBuiltin(BuiltinOperator_AVERAGE_POOL_2D, registration, ParsePool);
|
||||
}
|
||||
|
||||
TfLiteStatus AddBatchToSpaceNd() {
|
||||
return AddBuiltin(BuiltinOperator_BATCH_TO_SPACE_ND,
|
||||
Register_BATCH_TO_SPACE_ND(), ParseBatchToSpaceNd);
|
||||
}
|
||||
|
||||
TfLiteStatus AddBroadcastArgs() {
|
||||
return AddBuiltin(BuiltinOperator_BROADCAST_ARGS, Register_BROADCAST_ARGS(),
|
||||
ParseBroadcastArgs);
|
||||
}
|
||||
|
||||
TfLiteStatus AddBroadcastTo() {
|
||||
return AddBuiltin(BuiltinOperator_BROADCAST_TO, Register_BROADCAST_TO(),
|
||||
ParseBroadcastTo);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCallOnce() {
|
||||
return AddBuiltin(BuiltinOperator_CALL_ONCE, Register_CALL_ONCE(),
|
||||
ParseCallOnce);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCast() {
|
||||
return AddBuiltin(BuiltinOperator_CAST, Register_CAST(), ParseCast);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCeil() {
|
||||
return AddBuiltin(BuiltinOperator_CEIL, tflite::ops::micro::Register_CEIL(),
|
||||
ParseCeil);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCircularBuffer() {
|
||||
return AddCustom("CIRCULAR_BUFFER", tflite::Register_CIRCULAR_BUFFER());
|
||||
}
|
||||
|
||||
TfLiteStatus AddConcatenation() {
|
||||
return AddBuiltin(BuiltinOperator_CONCATENATION,
|
||||
tflite::ops::micro::Register_CONCATENATION(),
|
||||
ParseConcatenation);
|
||||
}
|
||||
|
||||
TfLiteStatus AddConv2D(
|
||||
const TfLiteRegistration& registration = Register_CONV_2D()) {
|
||||
return AddBuiltin(BuiltinOperator_CONV_2D, registration, ParseConv2D);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCos() {
|
||||
return AddBuiltin(BuiltinOperator_COS, tflite::ops::micro::Register_COS(),
|
||||
ParseCos);
|
||||
}
|
||||
|
||||
TfLiteStatus AddCumSum() {
|
||||
return AddBuiltin(BuiltinOperator_CUMSUM, tflite::Register_CUMSUM(),
|
||||
ParseCumsum);
|
||||
}
|
||||
|
||||
TfLiteStatus AddDepthToSpace() {
|
||||
return AddBuiltin(BuiltinOperator_DEPTH_TO_SPACE,
|
||||
tflite::Register_DEPTH_TO_SPACE(), ParseDepthToSpace);
|
||||
}
|
||||
|
||||
TfLiteStatus AddDepthwiseConv2D(
|
||||
const TfLiteRegistration& registration = Register_DEPTHWISE_CONV_2D()) {
|
||||
return AddBuiltin(BuiltinOperator_DEPTHWISE_CONV_2D, registration,
|
||||
ParseDepthwiseConv2D);
|
||||
}
|
||||
|
||||
TfLiteStatus AddDequantize() {
|
||||
return AddBuiltin(BuiltinOperator_DEQUANTIZE, tflite::Register_DEQUANTIZE(),
|
||||
ParseDequantize);
|
||||
}
|
||||
|
||||
TfLiteStatus AddDetectionPostprocess() {
|
||||
return AddCustom("TFLite_Detection_PostProcess",
|
||||
tflite::Register_DETECTION_POSTPROCESS());
|
||||
}
|
||||
|
||||
TfLiteStatus AddDiv() {
|
||||
return AddBuiltin(BuiltinOperator_DIV, tflite::Register_DIV(), ParseDiv);
|
||||
}
|
||||
|
||||
TfLiteStatus AddElu() {
|
||||
return AddBuiltin(BuiltinOperator_ELU, tflite::Register_ELU(), ParseElu);
|
||||
}
|
||||
|
||||
TfLiteStatus AddEqual() {
|
||||
return AddBuiltin(BuiltinOperator_EQUAL,
|
||||
tflite::ops::micro::Register_EQUAL(), ParseEqual);
|
||||
}
|
||||
|
||||
TfLiteStatus AddEthosU() {
|
||||
TfLiteRegistration* registration = tflite::Register_ETHOSU();
|
||||
if (registration) {
|
||||
return AddCustom(tflite::GetString_ETHOSU(), registration);
|
||||
}
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteStatus AddExp() {
|
||||
return AddBuiltin(BuiltinOperator_EXP, Register_EXP(), ParseExp);
|
||||
}
|
||||
|
||||
TfLiteStatus AddExpandDims() {
|
||||
return AddBuiltin(BuiltinOperator_EXPAND_DIMS, Register_EXPAND_DIMS(),
|
||||
ParseExpandDims);
|
||||
}
|
||||
|
||||
TfLiteStatus AddFill() {
|
||||
return AddBuiltin(BuiltinOperator_FILL, tflite::Register_FILL(), ParseFill);
|
||||
}
|
||||
|
||||
TfLiteStatus AddFloor() {
|
||||
return AddBuiltin(BuiltinOperator_FLOOR,
|
||||
tflite::ops::micro::Register_FLOOR(), ParseFloor);
|
||||
}
|
||||
|
||||
TfLiteStatus AddFloorDiv() {
|
||||
return AddBuiltin(BuiltinOperator_FLOOR_DIV, tflite::Register_FLOOR_DIV(),
|
||||
ParseFloorDiv);
|
||||
}
|
||||
|
||||
TfLiteStatus AddFloorMod() {
|
||||
return AddBuiltin(BuiltinOperator_FLOOR_MOD, tflite::Register_FLOOR_MOD(),
|
||||
ParseFloorMod);
|
||||
}
|
||||
|
||||
TfLiteStatus AddFullyConnected(
|
||||
const TfLiteRegistration& registration = Register_FULLY_CONNECTED()) {
|
||||
return AddBuiltin(BuiltinOperator_FULLY_CONNECTED, registration,
|
||||
ParseFullyConnected);
|
||||
}
|
||||
|
||||
TfLiteStatus AddGather() {
|
||||
return AddBuiltin(BuiltinOperator_GATHER, tflite::Register_GATHER(),
|
||||
ParseGather);
|
||||
}
|
||||
|
||||
TfLiteStatus AddGatherNd() {
|
||||
return AddBuiltin(BuiltinOperator_GATHER_ND, tflite::Register_GATHER_ND(),
|
||||
ParseGatherNd);
|
||||
}
|
||||
|
||||
TfLiteStatus AddGreater() {
|
||||
return AddBuiltin(BuiltinOperator_GREATER,
|
||||
tflite::ops::micro::Register_GREATER(), ParseGreater);
|
||||
}
|
||||
|
||||
TfLiteStatus AddGreaterEqual() {
|
||||
return AddBuiltin(BuiltinOperator_GREATER_EQUAL,
|
||||
tflite::ops::micro::Register_GREATER_EQUAL(),
|
||||
ParseGreaterEqual);
|
||||
}
|
||||
|
||||
TfLiteStatus AddHardSwish() {
|
||||
return AddBuiltin(BuiltinOperator_HARD_SWISH, tflite::Register_HARD_SWISH(),
|
||||
ParseHardSwish);
|
||||
}
|
||||
|
||||
TfLiteStatus AddIf() {
|
||||
return AddBuiltin(BuiltinOperator_IF, tflite::Register_IF(), ParseIf);
|
||||
}
|
||||
|
||||
TfLiteStatus AddL2Normalization() {
|
||||
return AddBuiltin(BuiltinOperator_L2_NORMALIZATION,
|
||||
tflite::ops::micro::Register_L2_NORMALIZATION(),
|
||||
ParseL2Normalization);
|
||||
}
|
||||
|
||||
TfLiteStatus AddL2Pool2D() {
|
||||
return AddBuiltin(BuiltinOperator_L2_POOL_2D, tflite::Register_L2_POOL_2D(),
|
||||
ParsePool);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLeakyRelu() {
|
||||
return AddBuiltin(BuiltinOperator_LEAKY_RELU, tflite::Register_LEAKY_RELU(),
|
||||
ParseLeakyRelu);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLess() {
|
||||
return AddBuiltin(BuiltinOperator_LESS, tflite::ops::micro::Register_LESS(),
|
||||
ParseLess);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLessEqual() {
|
||||
return AddBuiltin(BuiltinOperator_LESS_EQUAL,
|
||||
tflite::ops::micro::Register_LESS_EQUAL(),
|
||||
ParseLessEqual);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLog() {
|
||||
return AddBuiltin(BuiltinOperator_LOG, tflite::ops::micro::Register_LOG(),
|
||||
ParseLog);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLogicalAnd() {
|
||||
return AddBuiltin(BuiltinOperator_LOGICAL_AND,
|
||||
tflite::Register_LOGICAL_AND(), ParseLogicalAnd);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLogicalNot() {
|
||||
return AddBuiltin(BuiltinOperator_LOGICAL_NOT,
|
||||
tflite::ops::micro::Register_LOGICAL_NOT(),
|
||||
ParseLogicalNot);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLogicalOr() {
|
||||
return AddBuiltin(BuiltinOperator_LOGICAL_OR, tflite::Register_LOGICAL_OR(),
|
||||
ParseLogicalOr);
|
||||
}
|
||||
|
||||
TfLiteStatus AddLogistic() {
|
||||
return AddBuiltin(BuiltinOperator_LOGISTIC, tflite::Register_LOGISTIC(),
|
||||
ParseLogistic);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMaximum() {
|
||||
return AddBuiltin(BuiltinOperator_MAXIMUM,
|
||||
tflite::ops::micro::Register_MAXIMUM(), ParseMaximum);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMaxPool2D(
|
||||
const TfLiteRegistration& registration = Register_MAX_POOL_2D()) {
|
||||
return AddBuiltin(BuiltinOperator_MAX_POOL_2D, registration, ParsePool);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMirrorPad() {
|
||||
return AddBuiltin(BuiltinOperator_MIRROR_PAD, tflite::Register_MIRROR_PAD(),
|
||||
ParseMirrorPad);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMean() {
|
||||
return AddBuiltin(BuiltinOperator_MEAN, Register_MEAN(), ParseReducer);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMinimum() {
|
||||
return AddBuiltin(BuiltinOperator_MINIMUM,
|
||||
tflite::ops::micro::Register_MINIMUM(), ParseMinimum);
|
||||
}
|
||||
|
||||
TfLiteStatus AddMul(const TfLiteRegistration& registration = Register_MUL()) {
|
||||
return AddBuiltin(BuiltinOperator_MUL, registration, ParseMul);
|
||||
}
|
||||
|
||||
TfLiteStatus AddNeg() {
|
||||
return AddBuiltin(BuiltinOperator_NEG, tflite::ops::micro::Register_NEG(),
|
||||
ParseNeg);
|
||||
}
|
||||
|
||||
TfLiteStatus AddNotEqual() {
|
||||
return AddBuiltin(BuiltinOperator_NOT_EQUAL,
|
||||
tflite::ops::micro::Register_NOT_EQUAL(), ParseNotEqual);
|
||||
}
|
||||
|
||||
TfLiteStatus AddPack() {
|
||||
return AddBuiltin(BuiltinOperator_PACK, tflite::ops::micro::Register_PACK(),
|
||||
ParsePack);
|
||||
}
|
||||
|
||||
TfLiteStatus AddPad() {
|
||||
return AddBuiltin(BuiltinOperator_PAD, tflite::ops::micro::Register_PAD(),
|
||||
ParsePad);
|
||||
}
|
||||
|
||||
TfLiteStatus AddPadV2() {
|
||||
return AddBuiltin(BuiltinOperator_PADV2,
|
||||
tflite::ops::micro::Register_PADV2(), ParsePadV2);
|
||||
}
|
||||
|
||||
TfLiteStatus AddPrelu() {
|
||||
return AddBuiltin(BuiltinOperator_PRELU, tflite::Register_PRELU(),
|
||||
ParsePrelu);
|
||||
}
|
||||
|
||||
TfLiteStatus AddQuantize() {
|
||||
return AddBuiltin(BuiltinOperator_QUANTIZE, Register_QUANTIZE(),
|
||||
ParseQuantize);
|
||||
}
|
||||
|
||||
TfLiteStatus AddReadVariable() {
|
||||
return AddBuiltin(BuiltinOperator_READ_VARIABLE,
|
||||
tflite::Register_READ_VARIABLE(), ParseReadVariable);
|
||||
}
|
||||
|
||||
TfLiteStatus AddReduceMax() {
|
||||
return AddBuiltin(BuiltinOperator_REDUCE_MAX, Register_REDUCE_MAX(),
|
||||
ParseReducer);
|
||||
}
|
||||
|
||||
TfLiteStatus AddRelu() {
|
||||
return AddBuiltin(BuiltinOperator_RELU, tflite::Register_RELU(), ParseRelu);
|
||||
}
|
||||
|
||||
TfLiteStatus AddRelu6() {
|
||||
return AddBuiltin(BuiltinOperator_RELU6, tflite::Register_RELU6(),
|
||||
ParseRelu6);
|
||||
}
|
||||
|
||||
TfLiteStatus AddReshape() {
|
||||
return AddBuiltin(BuiltinOperator_RESHAPE,
|
||||
tflite::ops::micro::Register_RESHAPE(), ParseReshape);
|
||||
}
|
||||
|
||||
TfLiteStatus AddResizeBilinear() {
|
||||
return AddBuiltin(BuiltinOperator_RESIZE_BILINEAR,
|
||||
Register_RESIZE_BILINEAR(), ParseResizeBilinear);
|
||||
}
|
||||
|
||||
TfLiteStatus AddResizeNearestNeighbor() {
|
||||
return AddBuiltin(BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
|
||||
tflite::ops::micro::Register_RESIZE_NEAREST_NEIGHBOR(),
|
||||
ParseResizeNearestNeighbor);
|
||||
}
|
||||
|
||||
TfLiteStatus AddRound() {
|
||||
return AddBuiltin(BuiltinOperator_ROUND,
|
||||
tflite::ops::micro::Register_ROUND(), ParseRound);
|
||||
}
|
||||
|
||||
TfLiteStatus AddRsqrt() {
|
||||
return AddBuiltin(BuiltinOperator_RSQRT,
|
||||
tflite::ops::micro::Register_RSQRT(), ParseRsqrt);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSelectV2() {
|
||||
return AddBuiltin(BuiltinOperator_SELECT_V2, Register_SELECT_V2(),
|
||||
ParseSelectV2);
|
||||
}
|
||||
|
||||
TfLiteStatus AddShape() {
|
||||
return AddBuiltin(BuiltinOperator_SHAPE, Register_SHAPE(), ParseShape);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSin() {
|
||||
return AddBuiltin(BuiltinOperator_SIN, tflite::ops::micro::Register_SIN(),
|
||||
ParseSin);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSlice() {
|
||||
return AddBuiltin(BuiltinOperator_SLICE, Register_SLICE(), ParseSlice);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSoftmax(
|
||||
const TfLiteRegistration& registration = Register_SOFTMAX()) {
|
||||
return AddBuiltin(BuiltinOperator_SOFTMAX, registration, ParseSoftmax);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSpaceToBatchNd() {
|
||||
return AddBuiltin(BuiltinOperator_SPACE_TO_BATCH_ND,
|
||||
Register_SPACE_TO_BATCH_ND(), ParseSpaceToBatchNd);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSpaceToDepth() {
|
||||
return AddBuiltin(BuiltinOperator_SPACE_TO_DEPTH, Register_SPACE_TO_DEPTH(),
|
||||
ParseSpaceToDepth);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSplit() {
|
||||
return AddBuiltin(BuiltinOperator_SPLIT,
|
||||
tflite::ops::micro::Register_SPLIT(), ParseSplit);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSplitV() {
|
||||
return AddBuiltin(BuiltinOperator_SPLIT_V,
|
||||
tflite::ops::micro::Register_SPLIT_V(), ParseSplitV);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSqueeze() {
|
||||
return AddBuiltin(BuiltinOperator_SQUEEZE, Register_SQUEEZE(),
|
||||
ParseSqueeze);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSqrt() {
|
||||
return AddBuiltin(BuiltinOperator_SQRT, tflite::ops::micro::Register_SQRT(),
|
||||
ParseSqrt);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSquare() {
|
||||
return AddBuiltin(BuiltinOperator_SQUARE,
|
||||
tflite::ops::micro::Register_SQUARE(), ParseSquare);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSquaredDifference() {
|
||||
return AddBuiltin(BuiltinOperator_SQUARED_DIFFERENCE,
|
||||
tflite::Register_SQUARED_DIFFERENCE(),
|
||||
ParseSquaredDifference);
|
||||
}
|
||||
|
||||
TfLiteStatus AddStridedSlice() {
|
||||
return AddBuiltin(BuiltinOperator_STRIDED_SLICE,
|
||||
tflite::ops::micro::Register_STRIDED_SLICE(),
|
||||
ParseStridedSlice);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSub() {
|
||||
return AddBuiltin(BuiltinOperator_SUB, tflite::Register_SUB(), ParseSub);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSum() {
|
||||
return AddBuiltin(BuiltinOperator_SUM, Register_SUM(), ParseReducer);
|
||||
}
|
||||
|
||||
TfLiteStatus AddSvdf(
|
||||
const TfLiteRegistration& registration = Register_SVDF()) {
|
||||
return AddBuiltin(BuiltinOperator_SVDF, registration, ParseSvdf);
|
||||
}
|
||||
|
||||
TfLiteStatus AddTanh() {
|
||||
return AddBuiltin(BuiltinOperator_TANH, tflite::ops::micro::Register_TANH(),
|
||||
ParseTanh);
|
||||
}
|
||||
|
||||
TfLiteStatus AddTransposeConv() {
|
||||
return AddBuiltin(BuiltinOperator_TRANSPOSE_CONV,
|
||||
tflite::Register_TRANSPOSE_CONV(), ParseTransposeConv);
|
||||
}
|
||||
|
||||
TfLiteStatus AddTranspose() {
|
||||
return AddBuiltin(BuiltinOperator_TRANSPOSE, Register_TRANSPOSE(),
|
||||
ParseTranspose);
|
||||
}
|
||||
|
||||
TfLiteStatus AddUnpack() {
|
||||
return AddBuiltin(BuiltinOperator_UNPACK,
|
||||
tflite::ops::micro::Register_UNPACK(), ParseUnpack);
|
||||
}
|
||||
|
||||
TfLiteStatus AddUnidirectionalSequenceLSTM() {
|
||||
return AddBuiltin(BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM,
|
||||
Register_UNIDIRECTIONAL_SEQUENCE_LSTM(),
|
||||
ParseUnidirectionalSequenceLSTM);
|
||||
}
|
||||
|
||||
TfLiteStatus AddVarHandle() {
|
||||
return AddBuiltin(BuiltinOperator_VAR_HANDLE, Register_VAR_HANDLE(),
|
||||
ParseVarHandle);
|
||||
}
|
||||
|
||||
TfLiteStatus AddWhile() {
|
||||
return AddBuiltin(BuiltinOperator_WHILE, Register_WHILE(), ParseWhile);
|
||||
}
|
||||
|
||||
TfLiteStatus AddZerosLike() {
|
||||
return AddBuiltin(BuiltinOperator_ZEROS_LIKE, Register_ZEROS_LIKE(),
|
||||
ParseZerosLike);
|
||||
}
|
||||
|
||||
unsigned int GetRegistrationLength() { return registrations_len_; }
|
||||
|
||||
private:
|
||||
TfLiteStatus AddBuiltin(tflite::BuiltinOperator op,
|
||||
const TfLiteRegistration& registration,
|
||||
MicroOpResolver::BuiltinParseFunction parser) {
|
||||
if (op == BuiltinOperator_CUSTOM) {
|
||||
MicroPrintf("Invalid parameter BuiltinOperator_CUSTOM to the ");
|
||||
MicroPrintf("AddBuiltin function.");
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (FindOp(op) != nullptr) {
|
||||
MicroPrintf("Calling AddBuiltin with the same op more than ");
|
||||
MicroPrintf("once is not supported (Op: #%d).", op);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
if (registrations_len_ >= tOpCount) {
|
||||
MicroPrintf("Couldn't register builtin op #%d, resolver size ", op);
|
||||
MicroPrintf("is too small (%d).", tOpCount);
|
||||
return kTfLiteError;
|
||||
}
|
||||
|
||||
registrations_[registrations_len_] = registration;
|
||||
// Strictly speaking, the builtin_code is not necessary for TFLM but filling
|
||||
// it in regardless.
|
||||
registrations_[registrations_len_].builtin_code = op;
|
||||
registrations_len_++;
|
||||
|
||||
builtin_codes_[num_buitin_ops_] = op;
|
||||
builtin_parsers_[num_buitin_ops_] = parser;
|
||||
num_buitin_ops_++;
|
||||
|
||||
return kTfLiteOk;
|
||||
}
|
||||
|
||||
TfLiteRegistration registrations_[tOpCount];
|
||||
unsigned int registrations_len_ = 0;
|
||||
|
||||
// Arrays (and counter) to store the builtin codes and their corresponding
|
||||
// parse functions as these are registered with the Op Resolver.
|
||||
BuiltinOperator builtin_codes_[tOpCount];
|
||||
MicroOpResolver::BuiltinParseFunction builtin_parsers_[tOpCount];
|
||||
unsigned int num_buitin_ops_ = 0;
|
||||
|
||||
ErrorReporter* error_reporter_;
|
||||
};
|
||||
|
||||
}; // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_MICRO_MUTABLE_OP_RESOLVER_H_
|
||||
@@ -1,115 +0,0 @@
|
||||
/* Copyright 2020 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/micro/micro_profiler.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
#include "tensorflow/lite/micro/micro_time.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
uint32_t MicroProfiler::BeginEvent(const char* tag) {
|
||||
if (num_events_ == kMaxEvents) {
|
||||
num_events_ = 0;
|
||||
}
|
||||
|
||||
tags_[num_events_] = tag;
|
||||
start_ticks_[num_events_] = GetCurrentTimeTicks();
|
||||
end_ticks_[num_events_] = start_ticks_[num_events_] - 1;
|
||||
return num_events_++;
|
||||
}
|
||||
|
||||
void MicroProfiler::EndEvent(uint32_t event_handle) {
|
||||
TFLITE_DCHECK(event_handle < kMaxEvents);
|
||||
end_ticks_[event_handle] = GetCurrentTimeTicks();
|
||||
}
|
||||
|
||||
uint32_t MicroProfiler::GetTotalTicks() const {
|
||||
int32_t ticks = 0;
|
||||
for (int i = 0; i < num_events_; ++i) {
|
||||
ticks += end_ticks_[i] - start_ticks_[i];
|
||||
}
|
||||
return ticks;
|
||||
}
|
||||
|
||||
void MicroProfiler::Log() const {
|
||||
#if !defined(TF_LITE_STRIP_ERROR_STRINGS)
|
||||
for (int i = 0; i < num_events_; ++i) {
|
||||
uint32_t ticks = end_ticks_[i] - start_ticks_[i];
|
||||
MicroPrintf("%s took %" PRIu32 " ticks (%d ms).", tags_[i], ticks,
|
||||
TicksToMs(ticks));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MicroProfiler::LogCsv() const {
|
||||
#if !defined(TF_LITE_STRIP_ERROR_STRINGS)
|
||||
MicroPrintf("\"Event\",\"Tag\",\"Ticks\"");
|
||||
for (int i = 0; i < num_events_; ++i) {
|
||||
uint32_t ticks = end_ticks_[i] - start_ticks_[i];
|
||||
MicroPrintf("%d,%s,%" PRIu32, i, tags_[i], ticks);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MicroProfiler::LogTicksPerTagCsv() {
|
||||
#if !defined(TF_LITE_STRIP_ERROR_STRINGS)
|
||||
MicroPrintf(
|
||||
"\"Unique Tag\",\"Total ticks across all events with that tag.\"");
|
||||
int total_ticks = 0;
|
||||
for (int i = 0; i < num_events_; ++i) {
|
||||
uint32_t ticks = end_ticks_[i] - start_ticks_[i];
|
||||
TFLITE_DCHECK(tags_[i] != nullptr);
|
||||
int position = FindExistingOrNextPosition(tags_[i]);
|
||||
TFLITE_DCHECK(position >= 0);
|
||||
total_ticks_per_tag[position].tag = tags_[i];
|
||||
total_ticks_per_tag[position].ticks =
|
||||
total_ticks_per_tag[position].ticks + ticks;
|
||||
total_ticks += ticks;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_events_; ++i) {
|
||||
TicksPerTag each_tag_entry = total_ticks_per_tag[i];
|
||||
if (each_tag_entry.tag == nullptr) {
|
||||
break;
|
||||
}
|
||||
MicroPrintf("%s, %d", each_tag_entry.tag, each_tag_entry.ticks);
|
||||
}
|
||||
MicroPrintf("total number of ticks, %d", total_ticks);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This method finds a particular array element in the total_ticks_per_tag array
|
||||
// with the matching tag_name passed in the method. If it can find a
|
||||
// matching array element that has the same tag_name, then it will return the
|
||||
// position of the matching element. But if it unable to find a matching element
|
||||
// with the given tag_name, it will return the next available empty position
|
||||
// from the array.
|
||||
int MicroProfiler::FindExistingOrNextPosition(const char* tag_name) {
|
||||
int pos = 0;
|
||||
for (; pos < num_events_; pos++) {
|
||||
TicksPerTag each_tag_entry = total_ticks_per_tag[pos];
|
||||
if (each_tag_entry.tag == nullptr ||
|
||||
strcmp(each_tag_entry.tag, tag_name) == 0) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
return pos < num_events_ ? pos : -1;
|
||||
}
|
||||
} // namespace tflite
|
||||
@@ -1,139 +0,0 @@
|
||||
/* Copyright 2021 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.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef TENSORFLOW_LITE_MICRO_MICRO_PROFILER_H_
|
||||
#define TENSORFLOW_LITE_MICRO_MICRO_PROFILER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
// MicroProfiler creates a common way to gain fine-grained insight into runtime
|
||||
// performance. Bottleck operators can be identified along with slow code
|
||||
// sections. This can be used in conjunction with running the relevant micro
|
||||
// benchmark to evaluate end-to-end performance.
|
||||
class MicroProfiler {
|
||||
public:
|
||||
MicroProfiler() = default;
|
||||
virtual ~MicroProfiler() = default;
|
||||
|
||||
// Marks the start of a new event and returns an event handle that can be used
|
||||
// to mark the end of the event via EndEvent. The lifetime of the tag
|
||||
// parameter must exceed that of the MicroProfiler.
|
||||
virtual uint32_t BeginEvent(const char* tag);
|
||||
|
||||
// Marks the end of an event associated with event_handle. It is the
|
||||
// responsibility of the caller to ensure than EndEvent is called once and
|
||||
// only once per event_handle.
|
||||
//
|
||||
// If EndEvent is called more than once for the same event_handle, the last
|
||||
// call will be used as the end of event marker.If EndEvent is called 0 times
|
||||
// for a particular event_handle, the duration of that event will be 0 ticks.
|
||||
virtual void EndEvent(uint32_t event_handle);
|
||||
|
||||
// Clears all the events that have been currently profiled.
|
||||
void ClearEvents() { num_events_ = 0; }
|
||||
|
||||
// Returns the sum of the ticks taken across all the events. This number
|
||||
// is only meaningful if all of the events are disjoint (the end time of
|
||||
// event[i] <= start time of event[i+1]).
|
||||
uint32_t GetTotalTicks() const;
|
||||
|
||||
// Prints the profiling information of each of the events in human readable
|
||||
// form.
|
||||
void Log() const;
|
||||
|
||||
// Prints the profiling information of each of the events in CSV (Comma
|
||||
// Separated Value) form.
|
||||
void LogCsv() const;
|
||||
|
||||
// Prints total ticks for each unique tag in CSV format.
|
||||
// Output will have one row for each unique tag along with the
|
||||
// total ticks summed across all events with that particular tag.
|
||||
void LogTicksPerTagCsv();
|
||||
|
||||
private:
|
||||
// Maximum number of events that this class can keep track of. If we call
|
||||
// AddEvent more than kMaxEvents number of times, then the oldest event's
|
||||
// profiling information will be overwritten.
|
||||
static constexpr int kMaxEvents = 1024;
|
||||
|
||||
const char* tags_[kMaxEvents];
|
||||
uint32_t start_ticks_[kMaxEvents];
|
||||
uint32_t end_ticks_[kMaxEvents];
|
||||
int num_events_ = 0;
|
||||
|
||||
struct TicksPerTag {
|
||||
const char* tag;
|
||||
uint32_t ticks;
|
||||
};
|
||||
// In practice, the number of tags will be much lower than the number of
|
||||
// events. But it is theoretically possible that each event to be unique and
|
||||
// hence we allow total_ticks_per_tag to have kMaxEvents entries.
|
||||
TicksPerTag total_ticks_per_tag[kMaxEvents] = {};
|
||||
|
||||
int FindExistingOrNextPosition(const char* tag_name);
|
||||
|
||||
TF_LITE_REMOVE_VIRTUAL_DELETE;
|
||||
};
|
||||
|
||||
#if defined(TF_LITE_STRIP_ERROR_STRINGS)
|
||||
// For release builds, the ScopedMicroProfiler is a noop.
|
||||
//
|
||||
// This is done because the ScipedProfiler is used as part of the
|
||||
// MicroInterpreter and we want to ensure zero overhead for the release builds.
|
||||
class ScopedMicroProfiler {
|
||||
public:
|
||||
explicit ScopedMicroProfiler(const char* tag, MicroProfiler* profiler) {}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// This class can be used to add events to a MicroProfiler object that span the
|
||||
// lifetime of the ScopedMicroProfiler object.
|
||||
// Usage example:
|
||||
//
|
||||
// MicroProfiler profiler();
|
||||
// ...
|
||||
// {
|
||||
// ScopedMicroProfiler scoped_profiler("custom_tag", profiler);
|
||||
// work_to_profile();
|
||||
// }
|
||||
class ScopedMicroProfiler {
|
||||
public:
|
||||
explicit ScopedMicroProfiler(const char* tag, MicroProfiler* profiler)
|
||||
: profiler_(profiler) {
|
||||
if (profiler_ != nullptr) {
|
||||
event_handle_ = profiler_->BeginEvent(tag);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedMicroProfiler() {
|
||||
if (profiler_ != nullptr) {
|
||||
profiler_->EndEvent(event_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t event_handle_ = 0;
|
||||
MicroProfiler* profiler_ = nullptr;
|
||||
};
|
||||
#endif // !defined(TF_LITE_STRIP_ERROR_STRINGS)
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
#endif // TENSORFLOW_LITE_MICRO_MICRO_PROFILER_H_
|
||||
@@ -1,270 +0,0 @@
|
||||
/* Copyright 2020 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/micro/recording_micro_allocator.h"
|
||||
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/micro/arena_allocator/recording_single_arena_buffer_allocator.h"
|
||||
#include "tensorflow/lite/micro/compatibility.h"
|
||||
#include "tensorflow/lite/micro/memory_helpers.h"
|
||||
#include "tensorflow/lite/micro/memory_planner/greedy_memory_planner.h"
|
||||
#include "tensorflow/lite/micro/micro_allocator.h"
|
||||
#include "tensorflow/lite/micro/micro_error_reporter.h"
|
||||
|
||||
namespace tflite {
|
||||
|
||||
size_t RecordingMicroAllocator::GetDefaultTailUsage() {
|
||||
// RecordingMicroAllocator inherits from MicroAllocator and its tail usage is
|
||||
// similar with MicroAllocator with SingleArenaBufferAllocator and
|
||||
// MicroAllocator being replaced.
|
||||
// TODO(b/208703041): a template version of AlignSizeUp to make expression
|
||||
// shorter.
|
||||
return MicroAllocator::GetDefaultTailUsage(
|
||||
/*is_memory_planner_given=*/false) +
|
||||
AlignSizeUp(sizeof(RecordingSingleArenaBufferAllocator),
|
||||
alignof(RecordingSingleArenaBufferAllocator)) -
|
||||
AlignSizeUp(sizeof(SingleArenaBufferAllocator),
|
||||
alignof(SingleArenaBufferAllocator)) +
|
||||
AlignSizeUp(sizeof(RecordingMicroAllocator),
|
||||
alignof(RecordingMicroAllocator)) -
|
||||
AlignSizeUp(sizeof(MicroAllocator), alignof(MicroAllocator));
|
||||
}
|
||||
|
||||
RecordingMicroAllocator::RecordingMicroAllocator(
|
||||
RecordingSingleArenaBufferAllocator* recording_memory_allocator,
|
||||
MicroMemoryPlanner* memory_planner, ErrorReporter* error_reporter)
|
||||
: MicroAllocator(recording_memory_allocator, memory_planner,
|
||||
error_reporter),
|
||||
recording_memory_allocator_(recording_memory_allocator) {}
|
||||
|
||||
RecordingMicroAllocator* RecordingMicroAllocator::Create(
|
||||
uint8_t* tensor_arena, size_t arena_size, ErrorReporter* error_reporter) {
|
||||
TFLITE_DCHECK(error_reporter != nullptr);
|
||||
|
||||
RecordingSingleArenaBufferAllocator* simple_memory_allocator =
|
||||
RecordingSingleArenaBufferAllocator::Create(error_reporter, tensor_arena,
|
||||
arena_size);
|
||||
TFLITE_DCHECK(simple_memory_allocator != nullptr);
|
||||
|
||||
uint8_t* memory_planner_buffer =
|
||||
simple_memory_allocator->AllocatePersistentBuffer(
|
||||
sizeof(GreedyMemoryPlanner), alignof(GreedyMemoryPlanner));
|
||||
GreedyMemoryPlanner* memory_planner =
|
||||
new (memory_planner_buffer) GreedyMemoryPlanner();
|
||||
|
||||
uint8_t* allocator_buffer = simple_memory_allocator->AllocatePersistentBuffer(
|
||||
sizeof(RecordingMicroAllocator), alignof(RecordingMicroAllocator));
|
||||
RecordingMicroAllocator* allocator =
|
||||
new (allocator_buffer) RecordingMicroAllocator(
|
||||
simple_memory_allocator, memory_planner, error_reporter);
|
||||
return allocator;
|
||||
}
|
||||
|
||||
RecordedAllocation RecordingMicroAllocator::GetRecordedAllocation(
|
||||
RecordedAllocationType allocation_type) const {
|
||||
switch (allocation_type) {
|
||||
case RecordedAllocationType::kTfLiteEvalTensorData:
|
||||
return recorded_tflite_eval_tensor_data_;
|
||||
case RecordedAllocationType::kPersistentTfLiteTensorData:
|
||||
return recorded_persistent_tflite_tensor_data_;
|
||||
case RecordedAllocationType::kPersistentTfLiteTensorQuantizationData:
|
||||
return recorded_persistent_tflite_tensor_quantization_data_;
|
||||
case RecordedAllocationType::kPersistentBufferData:
|
||||
return recorded_persistent_buffer_data_;
|
||||
case RecordedAllocationType::kTfLiteTensorVariableBufferData:
|
||||
return recorded_tflite_tensor_variable_buffer_data_;
|
||||
case RecordedAllocationType::kNodeAndRegistrationArray:
|
||||
return recorded_node_and_registration_array_data_;
|
||||
case RecordedAllocationType::kOpData:
|
||||
return recorded_op_data_;
|
||||
}
|
||||
TF_LITE_REPORT_ERROR(error_reporter(), "Invalid allocation type supplied: %d",
|
||||
allocation_type);
|
||||
return RecordedAllocation();
|
||||
}
|
||||
|
||||
const RecordingSingleArenaBufferAllocator*
|
||||
RecordingMicroAllocator::GetSimpleMemoryAllocator() const {
|
||||
return recording_memory_allocator_;
|
||||
}
|
||||
|
||||
void RecordingMicroAllocator::PrintAllocations() const {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter(),
|
||||
"[RecordingMicroAllocator] Arena allocation total %d bytes",
|
||||
recording_memory_allocator_->GetUsedBytes());
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter(),
|
||||
"[RecordingMicroAllocator] Arena allocation head %d bytes",
|
||||
recording_memory_allocator_->GetNonPersistentUsedBytes());
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter(),
|
||||
"[RecordingMicroAllocator] Arena allocation tail %d bytes",
|
||||
recording_memory_allocator_->GetPersistentUsedBytes());
|
||||
PrintRecordedAllocation(RecordedAllocationType::kTfLiteEvalTensorData,
|
||||
"TfLiteEvalTensor data", "allocations");
|
||||
PrintRecordedAllocation(RecordedAllocationType::kPersistentTfLiteTensorData,
|
||||
"Persistent TfLiteTensor data", "tensors");
|
||||
PrintRecordedAllocation(
|
||||
RecordedAllocationType::kPersistentTfLiteTensorQuantizationData,
|
||||
"Persistent TfLiteTensor quantization data", "allocations");
|
||||
PrintRecordedAllocation(RecordedAllocationType::kPersistentBufferData,
|
||||
"Persistent buffer data", "allocations");
|
||||
PrintRecordedAllocation(
|
||||
RecordedAllocationType::kTfLiteTensorVariableBufferData,
|
||||
"TfLiteTensor variable buffer data", "allocations");
|
||||
PrintRecordedAllocation(RecordedAllocationType::kNodeAndRegistrationArray,
|
||||
"NodeAndRegistration struct",
|
||||
"NodeAndRegistration structs");
|
||||
PrintRecordedAllocation(RecordedAllocationType::kOpData,
|
||||
"Operator runtime data", "OpData structs");
|
||||
}
|
||||
|
||||
void* RecordingMicroAllocator::AllocatePersistentBuffer(size_t bytes) {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
void* buffer = MicroAllocator::AllocatePersistentBuffer(bytes);
|
||||
RecordAllocationUsage(allocations, recorded_persistent_buffer_data_);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void RecordingMicroAllocator::PrintRecordedAllocation(
|
||||
RecordedAllocationType allocation_type, const char* allocation_name,
|
||||
const char* allocation_description) const {
|
||||
#ifndef TF_LITE_STRIP_ERROR_STRINGS
|
||||
RecordedAllocation allocation = GetRecordedAllocation(allocation_type);
|
||||
if (allocation.used_bytes > 0 || allocation.requested_bytes > 0) {
|
||||
TF_LITE_REPORT_ERROR(
|
||||
error_reporter(),
|
||||
"[RecordingMicroAllocator] '%s' used %d bytes with alignment overhead "
|
||||
"(requested %d bytes for %d %s)",
|
||||
allocation_name, allocation.used_bytes, allocation.requested_bytes,
|
||||
allocation.count, allocation_description);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TfLiteStatus RecordingMicroAllocator::AllocateNodeAndRegistrations(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations) {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
|
||||
TfLiteStatus status =
|
||||
MicroAllocator::AllocateNodeAndRegistrations(model, subgraph_allocations);
|
||||
|
||||
RecordAllocationUsage(allocations,
|
||||
recorded_node_and_registration_array_data_);
|
||||
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
// The allocation count in SingleArenaBufferAllocator will only be 1. To
|
||||
// provide better logging, decrement by 1 and add in the actual number of
|
||||
// operators used in the graph: The allocation for this recording will
|
||||
// always be 1. This is because the parent class mallocs one large
|
||||
// allocation for the number of nodes in the graph (e.g.
|
||||
// sizeof(NodeAndRegistration) * num_nodes). To prevent extra overhead and
|
||||
// potential for fragmentation, manually adjust the accounting by
|
||||
// decrementing by 1 and adding the actual number of nodes used in the
|
||||
// graph:
|
||||
if (model->subgraphs()->Get(subgraph_idx)->operators()) {
|
||||
recorded_node_and_registration_array_data_.count +=
|
||||
model->subgraphs()->Get(subgraph_idx)->operators()->size() - 1;
|
||||
} else {
|
||||
recorded_node_and_registration_array_data_.count -= 1;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
TfLiteStatus RecordingMicroAllocator::AllocateTfLiteEvalTensors(
|
||||
const Model* model, SubgraphAllocations* subgraph_allocations) {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
|
||||
TfLiteStatus status =
|
||||
MicroAllocator::AllocateTfLiteEvalTensors(model, subgraph_allocations);
|
||||
|
||||
RecordAllocationUsage(allocations, recorded_tflite_eval_tensor_data_);
|
||||
|
||||
for (size_t subgraph_idx = 0; subgraph_idx < model->subgraphs()->size();
|
||||
subgraph_idx++) {
|
||||
// The allocation for this recording will always be 1. This is because the
|
||||
// parent class mallocs one large allocation for the number of tensors in
|
||||
// the graph (e.g. sizeof(TfLiteEvalTensor) * num_tensors). To prevent extra
|
||||
// overhead and potential for fragmentation, manually adjust the accounting
|
||||
// by decrementing by 1 and adding the actual number of tensors used in the
|
||||
// graph:
|
||||
recorded_tflite_eval_tensor_data_.count +=
|
||||
model->subgraphs()->Get(subgraph_idx)->tensors()->size() - 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
TfLiteStatus RecordingMicroAllocator::AllocateVariables(
|
||||
const SubGraph* subgraph, TfLiteEvalTensor* eval_tensors) {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
|
||||
TfLiteStatus status =
|
||||
MicroAllocator::AllocateVariables(subgraph, eval_tensors);
|
||||
|
||||
RecordAllocationUsage(allocations,
|
||||
recorded_tflite_tensor_variable_buffer_data_);
|
||||
return status;
|
||||
}
|
||||
|
||||
TfLiteTensor*
|
||||
RecordingMicroAllocator::AllocatePersistentTfLiteTensorInternal() {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
|
||||
TfLiteTensor* result =
|
||||
MicroAllocator::AllocatePersistentTfLiteTensorInternal();
|
||||
|
||||
RecordAllocationUsage(allocations, recorded_persistent_tflite_tensor_data_);
|
||||
return result;
|
||||
}
|
||||
|
||||
TfLiteStatus RecordingMicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
|
||||
const Model* model, TfLiteTensor* tensor, int tensor_index,
|
||||
int subgraph_index, bool allocate_temp) {
|
||||
RecordedAllocation allocations = SnapshotAllocationUsage();
|
||||
|
||||
TfLiteStatus status = MicroAllocator::PopulateTfLiteTensorFromFlatbuffer(
|
||||
model, tensor, tensor_index, subgraph_index, allocate_temp);
|
||||
|
||||
RecordAllocationUsage(allocations,
|
||||
recorded_persistent_tflite_tensor_quantization_data_);
|
||||
return status;
|
||||
}
|
||||
|
||||
RecordedAllocation RecordingMicroAllocator::SnapshotAllocationUsage() const {
|
||||
return {/*requested_bytes=*/recording_memory_allocator_->GetRequestedBytes(),
|
||||
/*used_bytes=*/recording_memory_allocator_->GetUsedBytes(),
|
||||
/*count=*/recording_memory_allocator_->GetAllocatedCount()};
|
||||
}
|
||||
|
||||
void RecordingMicroAllocator::RecordAllocationUsage(
|
||||
const RecordedAllocation& snapshotted_allocation,
|
||||
RecordedAllocation& recorded_allocation) {
|
||||
recorded_allocation.requested_bytes +=
|
||||
recording_memory_allocator_->GetRequestedBytes() -
|
||||
snapshotted_allocation.requested_bytes;
|
||||
recorded_allocation.used_bytes +=
|
||||
recording_memory_allocator_->GetUsedBytes() -
|
||||
snapshotted_allocation.used_bytes;
|
||||
recorded_allocation.count +=
|
||||
recording_memory_allocator_->GetAllocatedCount() -
|
||||
snapshotted_allocation.count;
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,496 +0,0 @@
|
||||
#ifndef FLATBUFFERS_BASE_H_
|
||||
#define FLATBUFFERS_BASE_H_
|
||||
|
||||
// For TFLM, we always want FLATBUFFERS_LOCALE_INDEPENDENT to be defined as 0.
|
||||
// We could achieve this by adding -DFLATBUFFERS_LOCALE_INDEPENDENT=0 to the
|
||||
// TFLM Makefile. However, for (at least) the Arduino, adding additional build
|
||||
// flags during the compilation can be a bit awkward. As such, we have instead
|
||||
// made a decision to change the default to be FLATBUFFERS_LOCALE_INDEPENDENT=0
|
||||
// for TFLM to make it easier for external IDE integration.
|
||||
#ifndef FLATBUFFERS_LOCALE_INDEPENDENT
|
||||
#define FLATBUFFERS_LOCALE_INDEPENDENT 0
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
|
||||
// If activate should be declared and included first.
|
||||
#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \
|
||||
defined(_MSC_VER) && defined(_DEBUG)
|
||||
// The _CRTDBG_MAP_ALLOC inside <crtdbg.h> will replace
|
||||
// calloc/free (etc) to its debug version using #define directives.
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
// Replace operator new by trace-enabled version.
|
||||
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
|
||||
#define new DEBUG_NEW
|
||||
#endif
|
||||
|
||||
#if !defined(FLATBUFFERS_ASSERT)
|
||||
#include <assert.h>
|
||||
#define FLATBUFFERS_ASSERT assert
|
||||
#elif defined(FLATBUFFERS_ASSERT_INCLUDE)
|
||||
// Include file with forward declaration
|
||||
#include FLATBUFFERS_ASSERT_INCLUDE
|
||||
#endif
|
||||
|
||||
#ifndef ARDUINO
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H)
|
||||
#include <utility.h>
|
||||
#else
|
||||
#include <utility>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#if defined(__unix__) && !defined(FLATBUFFERS_LOCALE_INDEPENDENT)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/api-level.h>
|
||||
#endif
|
||||
|
||||
#if defined(__ICCARM__)
|
||||
#include <intrinsics.h>
|
||||
#endif
|
||||
|
||||
// Note the __clang__ check is needed, because clang presents itself
|
||||
// as an older GNUC compiler (4.2).
|
||||
// Clang 3.3 and later implement all of the ISO C++ 2011 standard.
|
||||
// Clang 3.4 and later implement all of the ISO C++ 2014 standard.
|
||||
// http://clang.llvm.org/cxx_status.html
|
||||
|
||||
// Note the MSVC value '__cplusplus' may be incorrect:
|
||||
// The '__cplusplus' predefined macro in the MSVC stuck at the value 199711L,
|
||||
// indicating (erroneously!) that the compiler conformed to the C++98 Standard.
|
||||
// This value should be correct starting from MSVC2017-15.7-Preview-3.
|
||||
// The '__cplusplus' will be valid only if MSVC2017-15.7-P3 and the `/Zc:__cplusplus` switch is set.
|
||||
// Workaround (for details see MSDN):
|
||||
// Use the _MSC_VER and _MSVC_LANG definition instead of the __cplusplus for compatibility.
|
||||
// The _MSVC_LANG macro reports the Standard version regardless of the '/Zc:__cplusplus' switch.
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#define FLATBUFFERS_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
#define FLATBUFFERS_GCC 0
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
#define FLATBUFFERS_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__)
|
||||
#else
|
||||
#define FLATBUFFERS_CLANG 0
|
||||
#endif
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
#if __cplusplus <= 199711L && \
|
||||
(!defined(_MSC_VER) || _MSC_VER < 1600) && \
|
||||
(!defined(__GNUC__) || \
|
||||
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400))
|
||||
#error A C++11 compatible compiler with support for the auto typing is \
|
||||
required for FlatBuffers.
|
||||
#error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__
|
||||
#endif
|
||||
|
||||
#if !defined(__clang__) && \
|
||||
defined(__GNUC__) && \
|
||||
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600)
|
||||
// Backwards compatibility for g++ 4.4, and 4.5 which don't have the nullptr
|
||||
// and constexpr keywords. Note the __clang__ check is needed, because clang
|
||||
// presents itself as an older GNUC compiler.
|
||||
#ifndef nullptr_t
|
||||
const class nullptr_t {
|
||||
public:
|
||||
template<class T> inline operator T*() const { return 0; }
|
||||
private:
|
||||
void operator&() const;
|
||||
} nullptr = {};
|
||||
#endif
|
||||
#ifndef constexpr
|
||||
#define constexpr const
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The wire format uses a little endian encoding (since that's efficient for
|
||||
// the common platforms).
|
||||
#if defined(__s390x__)
|
||||
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||
#endif // __s390x__
|
||||
#if !defined(FLATBUFFERS_LITTLEENDIAN)
|
||||
#if defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__)
|
||||
#if (defined(__BIG_ENDIAN__) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
||||
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||
#else
|
||||
#define FLATBUFFERS_LITTLEENDIAN 1
|
||||
#endif // __BIG_ENDIAN__
|
||||
#elif defined(_MSC_VER)
|
||||
#if defined(_M_PPC)
|
||||
#define FLATBUFFERS_LITTLEENDIAN 0
|
||||
#else
|
||||
#define FLATBUFFERS_LITTLEENDIAN 1
|
||||
#endif
|
||||
#else
|
||||
#error Unable to determine endianness, define FLATBUFFERS_LITTLEENDIAN.
|
||||
#endif
|
||||
#endif // !defined(FLATBUFFERS_LITTLEENDIAN)
|
||||
|
||||
#define FLATBUFFERS_VERSION_MAJOR 2
|
||||
#define FLATBUFFERS_VERSION_MINOR 0
|
||||
#define FLATBUFFERS_VERSION_REVISION 6
|
||||
#define FLATBUFFERS_STRING_EXPAND(X) #X
|
||||
#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X)
|
||||
namespace flatbuffers {
|
||||
// Returns version as string "MAJOR.MINOR.REVISION".
|
||||
const char* FLATBUFFERS_VERSION();
|
||||
}
|
||||
|
||||
#if (!defined(_MSC_VER) || _MSC_VER > 1600) && \
|
||||
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 407)) || \
|
||||
defined(__clang__)
|
||||
#define FLATBUFFERS_FINAL_CLASS final
|
||||
#define FLATBUFFERS_OVERRIDE override
|
||||
#define FLATBUFFERS_EXPLICIT_CPP11 explicit
|
||||
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t
|
||||
#else
|
||||
#define FLATBUFFERS_FINAL_CLASS
|
||||
#define FLATBUFFERS_OVERRIDE
|
||||
#define FLATBUFFERS_EXPLICIT_CPP11
|
||||
#define FLATBUFFERS_VTABLE_UNDERLYING_TYPE
|
||||
#endif
|
||||
|
||||
#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \
|
||||
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \
|
||||
(defined(__cpp_constexpr) && __cpp_constexpr >= 200704)
|
||||
#define FLATBUFFERS_CONSTEXPR constexpr
|
||||
#define FLATBUFFERS_CONSTEXPR_CPP11 constexpr
|
||||
#define FLATBUFFERS_CONSTEXPR_DEFINED
|
||||
#else
|
||||
#define FLATBUFFERS_CONSTEXPR const
|
||||
#define FLATBUFFERS_CONSTEXPR_CPP11
|
||||
#endif
|
||||
|
||||
#if (defined(__cplusplus) && __cplusplus >= 201402L) || \
|
||||
(defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
|
||||
#define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11
|
||||
#else
|
||||
#define FLATBUFFERS_CONSTEXPR_CPP14
|
||||
#endif
|
||||
|
||||
#if (defined(__GXX_EXPERIMENTAL_CXX0X__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \
|
||||
(defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023026)) || \
|
||||
defined(__clang__)
|
||||
#define FLATBUFFERS_NOEXCEPT noexcept
|
||||
#else
|
||||
#define FLATBUFFERS_NOEXCEPT
|
||||
#endif
|
||||
|
||||
// NOTE: the FLATBUFFERS_DELETE_FUNC macro may change the access mode to
|
||||
// private, so be sure to put it at the end or reset access mode explicitly.
|
||||
#if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \
|
||||
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \
|
||||
defined(__clang__)
|
||||
#define FLATBUFFERS_DELETE_FUNC(func) func = delete
|
||||
#else
|
||||
#define FLATBUFFERS_DELETE_FUNC(func) private: func
|
||||
#endif
|
||||
|
||||
#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \
|
||||
(!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \
|
||||
defined(__clang__)
|
||||
#define FLATBUFFERS_DEFAULT_DECLARATION
|
||||
#endif
|
||||
|
||||
// Check if we can use template aliases
|
||||
// Not possible if Microsoft Compiler before 2012
|
||||
// Possible is the language feature __cpp_alias_templates is defined well
|
||||
// Or possible if the C++ std is C+11 or newer
|
||||
#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \
|
||||
|| (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \
|
||||
|| (defined(__cplusplus) && __cplusplus >= 201103L)
|
||||
#define FLATBUFFERS_TEMPLATES_ALIASES
|
||||
#endif
|
||||
|
||||
#ifndef FLATBUFFERS_HAS_STRING_VIEW
|
||||
// Only provide flatbuffers::string_view if __has_include can be used
|
||||
// to detect a header that provides an implementation
|
||||
#if defined(__has_include)
|
||||
// Check for std::string_view (in c++17)
|
||||
#if __has_include(<string_view>) && (__cplusplus >= 201606 || (defined(_HAS_CXX17) && _HAS_CXX17))
|
||||
#include <string_view>
|
||||
namespace flatbuffers {
|
||||
typedef std::string_view string_view;
|
||||
}
|
||||
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||
// Check for std::experimental::string_view (in c++14, compiler-dependent)
|
||||
#elif __has_include(<experimental/string_view>) && (__cplusplus >= 201411)
|
||||
#include <experimental/string_view>
|
||||
namespace flatbuffers {
|
||||
typedef std::experimental::string_view string_view;
|
||||
}
|
||||
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||
// Check for absl::string_view
|
||||
#elif __has_include("absl/strings/string_view.h")
|
||||
#include "absl/strings/string_view.h"
|
||||
namespace flatbuffers {
|
||||
typedef absl::string_view string_view;
|
||||
}
|
||||
#define FLATBUFFERS_HAS_STRING_VIEW 1
|
||||
#endif
|
||||
#endif // __has_include
|
||||
#endif // !FLATBUFFERS_HAS_STRING_VIEW
|
||||
|
||||
#ifndef FLATBUFFERS_GENERAL_HEAP_ALLOC_OK
|
||||
// Allow heap allocations to be used
|
||||
#define FLATBUFFERS_GENERAL_HEAP_ALLOC_OK 1
|
||||
#endif // !FLATBUFFERS_GENERAL_HEAP_ALLOC_OK
|
||||
|
||||
#ifndef FLATBUFFERS_HAS_NEW_STRTOD
|
||||
// Modern (C++11) strtod and strtof functions are available for use.
|
||||
// 1) nan/inf strings as argument of strtod;
|
||||
// 2) hex-float as argument of strtod/strtof.
|
||||
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || \
|
||||
(defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \
|
||||
(defined(__clang__))
|
||||
#define FLATBUFFERS_HAS_NEW_STRTOD 1
|
||||
#endif
|
||||
#endif // !FLATBUFFERS_HAS_NEW_STRTOD
|
||||
|
||||
#ifndef FLATBUFFERS_LOCALE_INDEPENDENT
|
||||
// Enable locale independent functions {strtof_l, strtod_l,strtoll_l,
|
||||
// strtoull_l}.
|
||||
#if (defined(_MSC_VER) && _MSC_VER >= 1800) || \
|
||||
(defined(__ANDROID_API__) && __ANDROID_API__>= 21) || \
|
||||
(defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)) && \
|
||||
(!defined(__Fuchsia__) && !defined(__ANDROID_API__))
|
||||
#define FLATBUFFERS_LOCALE_INDEPENDENT 1
|
||||
#else
|
||||
#define FLATBUFFERS_LOCALE_INDEPENDENT 0
|
||||
#endif
|
||||
#endif // !FLATBUFFERS_LOCALE_INDEPENDENT
|
||||
|
||||
// Suppress Undefined Behavior Sanitizer (recoverable only). Usage:
|
||||
// - __supress_ubsan__("undefined")
|
||||
// - __supress_ubsan__("signed-integer-overflow")
|
||||
#if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7))
|
||||
#define __supress_ubsan__(type) __attribute__((no_sanitize(type)))
|
||||
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)
|
||||
#define __supress_ubsan__(type) __attribute__((no_sanitize_undefined))
|
||||
#else
|
||||
#define __supress_ubsan__(type)
|
||||
#endif
|
||||
|
||||
// This is constexpr function used for checking compile-time constants.
|
||||
// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`.
|
||||
template<typename T> FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) {
|
||||
return !!t;
|
||||
}
|
||||
|
||||
// Enable C++ attribute [[]] if std:c++17 or higher.
|
||||
#if ((__cplusplus >= 201703L) \
|
||||
|| (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)))
|
||||
// All attributes unknown to an implementation are ignored without causing an error.
|
||||
#define FLATBUFFERS_ATTRIBUTE(attr) attr
|
||||
|
||||
#define FLATBUFFERS_FALLTHROUGH() [[fallthrough]]
|
||||
#else
|
||||
#define FLATBUFFERS_ATTRIBUTE(attr)
|
||||
|
||||
#if FLATBUFFERS_CLANG >= 30800
|
||||
#define FLATBUFFERS_FALLTHROUGH() [[clang::fallthrough]]
|
||||
#elif FLATBUFFERS_GCC >= 70300
|
||||
#define FLATBUFFERS_FALLTHROUGH() [[gnu::fallthrough]]
|
||||
#else
|
||||
#define FLATBUFFERS_FALLTHROUGH()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// @endcond
|
||||
|
||||
/// @file
|
||||
namespace flatbuffers {
|
||||
|
||||
/// @cond FLATBUFFERS_INTERNAL
|
||||
// Our default offset / size type, 32bit on purpose on 64bit systems.
|
||||
// Also, using a consistent offset type maintains compatibility of serialized
|
||||
// offset values between 32bit and 64bit systems.
|
||||
typedef uint32_t uoffset_t;
|
||||
|
||||
// Signed offsets for references that can go in both directions.
|
||||
typedef int32_t soffset_t;
|
||||
|
||||
// Offset/index used in v-tables, can be changed to uint8_t in
|
||||
// format forks to save a bit of space if desired.
|
||||
typedef uint16_t voffset_t;
|
||||
|
||||
typedef uintmax_t largest_scalar_t;
|
||||
|
||||
// In 32bits, this evaluates to 2GB - 1
|
||||
#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(::flatbuffers::soffset_t) * 8 - 1)) - 1)
|
||||
|
||||
// The minimum size buffer that can be a valid flatbuffer.
|
||||
// Includes the offset to the root table (uoffset_t), the offset to the vtable
|
||||
// of the root table (soffset_t), the size of the vtable (uint16_t), and the
|
||||
// size of the referring table (uint16_t).
|
||||
#define FLATBUFFERS_MIN_BUFFER_SIZE sizeof(uoffset_t) + sizeof(soffset_t) + \
|
||||
sizeof(uint16_t) + sizeof(uint16_t)
|
||||
|
||||
// We support aligning the contents of buffers up to this size.
|
||||
#ifndef FLATBUFFERS_MAX_ALIGNMENT
|
||||
#define FLATBUFFERS_MAX_ALIGNMENT 32
|
||||
#endif
|
||||
|
||||
/// @brief The length of a FlatBuffer file header.
|
||||
static const size_t kFileIdentifierLength = 4;
|
||||
|
||||
inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) {
|
||||
return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) &&
|
||||
(align & (align - 1)) == 0; // must be power of 2
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable: 4351) // C4351: new behavior: elements of array ... will be default initialized
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127) // C4127: conditional expression is constant
|
||||
#endif
|
||||
|
||||
template<typename T> T EndianSwap(T t) {
|
||||
#if defined(_MSC_VER)
|
||||
#define FLATBUFFERS_BYTESWAP16 _byteswap_ushort
|
||||
#define FLATBUFFERS_BYTESWAP32 _byteswap_ulong
|
||||
#define FLATBUFFERS_BYTESWAP64 _byteswap_uint64
|
||||
#elif defined(__ICCARM__)
|
||||
#define FLATBUFFERS_BYTESWAP16 __REV16
|
||||
#define FLATBUFFERS_BYTESWAP32 __REV
|
||||
#define FLATBUFFERS_BYTESWAP64(x) \
|
||||
((__REV(static_cast<uint32_t>(x >> 32U))) | (static_cast<uint64_t>(__REV(static_cast<uint32_t>(x)))) << 32U)
|
||||
#else
|
||||
#if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ < 408 && !defined(__clang__)
|
||||
// __builtin_bswap16 was missing prior to GCC 4.8.
|
||||
#define FLATBUFFERS_BYTESWAP16(x) \
|
||||
static_cast<uint16_t>(__builtin_bswap32(static_cast<uint32_t>(x) << 16))
|
||||
#else
|
||||
#define FLATBUFFERS_BYTESWAP16 __builtin_bswap16
|
||||
#endif
|
||||
#define FLATBUFFERS_BYTESWAP32 __builtin_bswap32
|
||||
#define FLATBUFFERS_BYTESWAP64 __builtin_bswap64
|
||||
#endif
|
||||
if (sizeof(T) == 1) { // Compile-time if-then's.
|
||||
return t;
|
||||
} else if (sizeof(T) == 2) {
|
||||
union { T t; uint16_t i; } u = { t };
|
||||
u.i = FLATBUFFERS_BYTESWAP16(u.i);
|
||||
return u.t;
|
||||
} else if (sizeof(T) == 4) {
|
||||
union { T t; uint32_t i; } u = { t };
|
||||
u.i = FLATBUFFERS_BYTESWAP32(u.i);
|
||||
return u.t;
|
||||
} else if (sizeof(T) == 8) {
|
||||
union { T t; uint64_t i; } u = { t };
|
||||
u.i = FLATBUFFERS_BYTESWAP64(u.i);
|
||||
return u.t;
|
||||
} else {
|
||||
FLATBUFFERS_ASSERT(0);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T> T EndianScalar(T t) {
|
||||
#if FLATBUFFERS_LITTLEENDIAN
|
||||
return t;
|
||||
#else
|
||||
return EndianSwap(t);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details.
|
||||
__supress_ubsan__("alignment")
|
||||
T ReadScalar(const void *p) {
|
||||
return EndianScalar(*reinterpret_cast<const T *>(p));
|
||||
}
|
||||
|
||||
// See https://github.com/google/flatbuffers/issues/5950
|
||||
|
||||
#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
// UBSAN: C++ aliasing type rules, see std::bit_cast<> for details.
|
||||
__supress_ubsan__("alignment")
|
||||
void WriteScalar(void *p, T t) {
|
||||
*reinterpret_cast<T *>(p) = EndianScalar(t);
|
||||
}
|
||||
|
||||
template<typename T> struct Offset;
|
||||
template<typename T> __supress_ubsan__("alignment") void WriteScalar(void *p, Offset<T> t) {
|
||||
*reinterpret_cast<uoffset_t *>(p) = EndianScalar(t.o);
|
||||
}
|
||||
|
||||
#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
// Computes how many bytes you'd have to pad to be able to write an
|
||||
// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in
|
||||
// memory).
|
||||
__supress_ubsan__("unsigned-integer-overflow")
|
||||
inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) {
|
||||
return ((~buf_size) + 1) & (scalar_size - 1);
|
||||
}
|
||||
|
||||
// Generic 'operator==' with conditional specialisations.
|
||||
// T e - new value of a scalar field.
|
||||
// T def - default of scalar (is known at compile-time).
|
||||
template<typename T> inline bool IsTheSameAs(T e, T def) { return e == def; }
|
||||
|
||||
#if defined(FLATBUFFERS_NAN_DEFAULTS) && \
|
||||
defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
|
||||
// Like `operator==(e, def)` with weak NaN if T=(float|double).
|
||||
template<typename T> inline bool IsFloatTheSameAs(T e, T def) {
|
||||
return (e == def) || ((def != def) && (e != e));
|
||||
}
|
||||
template<> inline bool IsTheSameAs<float>(float e, float def) {
|
||||
return IsFloatTheSameAs(e, def);
|
||||
}
|
||||
template<> inline bool IsTheSameAs<double>(double e, double def) {
|
||||
return IsFloatTheSameAs(e, def);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check 'v' is out of closed range [low; high].
|
||||
// Workaround for GCC warning [-Werror=type-limits]:
|
||||
// comparison is always true due to limited range of data type.
|
||||
template<typename T>
|
||||
inline bool IsOutRange(const T &v, const T &low, const T &high) {
|
||||
return (v < low) || (high < v);
|
||||
}
|
||||
|
||||
// Check 'v' is in closed range [low; high].
|
||||
template<typename T>
|
||||
inline bool IsInRange(const T &v, const T &low, const T &high) {
|
||||
return !IsOutRange(v, low, high);
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
#endif // FLATBUFFERS_BASE_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_H_
|
||||
#define FLATBUFFERS_H_
|
||||
|
||||
// TODO: These includes are for mitigating the pains of users editing their
|
||||
// source because they relied on flatbuffers.h to include everything for them.
|
||||
#include "flatbuffers/array.h"
|
||||
#include "flatbuffers/base.h"
|
||||
#include "flatbuffers/buffer.h"
|
||||
#include "flatbuffers/buffer_ref.h"
|
||||
#include "flatbuffers/detached_buffer.h"
|
||||
#include "flatbuffers/flatbuffer_builder.h"
|
||||
#include "flatbuffers/stl_emulation.h"
|
||||
#include "flatbuffers/string.h"
|
||||
#include "flatbuffers/struct.h"
|
||||
#include "flatbuffers/table.h"
|
||||
#include "flatbuffers/vector.h"
|
||||
#include "flatbuffers/vector_downward.h"
|
||||
#include "flatbuffers/verifier.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
/// @brief This can compute the start of a FlatBuffer from a root pointer, i.e.
|
||||
/// it is the opposite transformation of GetRoot().
|
||||
/// This may be useful if you want to pass on a root and have the recipient
|
||||
/// delete the buffer afterwards.
|
||||
inline const uint8_t *GetBufferStartFromRootPointer(const void *root) {
|
||||
auto table = reinterpret_cast<const Table *>(root);
|
||||
auto vtable = table->GetVTable();
|
||||
// Either the vtable is before the root or after the root.
|
||||
auto start = (std::min)(vtable, reinterpret_cast<const uint8_t *>(root));
|
||||
// Align to at least sizeof(uoffset_t).
|
||||
start = reinterpret_cast<const uint8_t *>(reinterpret_cast<uintptr_t>(start) &
|
||||
~(sizeof(uoffset_t) - 1));
|
||||
// Additionally, there may be a file_identifier in the buffer, and the root
|
||||
// offset. The buffer may have been aligned to any size between
|
||||
// sizeof(uoffset_t) and FLATBUFFERS_MAX_ALIGNMENT (see "force_align").
|
||||
// Sadly, the exact alignment is only known when constructing the buffer,
|
||||
// since it depends on the presence of values with said alignment properties.
|
||||
// So instead, we simply look at the next uoffset_t values (root,
|
||||
// file_identifier, and alignment padding) to see which points to the root.
|
||||
// None of the other values can "impersonate" the root since they will either
|
||||
// be 0 or four ASCII characters.
|
||||
static_assert(flatbuffers::kFileIdentifierLength == sizeof(uoffset_t),
|
||||
"file_identifier is assumed to be the same size as uoffset_t");
|
||||
for (auto possible_roots = FLATBUFFERS_MAX_ALIGNMENT / sizeof(uoffset_t) + 1;
|
||||
possible_roots; possible_roots--) {
|
||||
start -= sizeof(uoffset_t);
|
||||
if (ReadScalar<uoffset_t>(start) + start ==
|
||||
reinterpret_cast<const uint8_t *>(root))
|
||||
return start;
|
||||
}
|
||||
// We didn't find the root, either the "root" passed isn't really a root,
|
||||
// or the buffer is corrupt.
|
||||
// Assert, because calling this function with bad data may cause reads
|
||||
// outside of buffer boundaries.
|
||||
FLATBUFFERS_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// @brief This return the prefixed size of a FlatBuffer.
|
||||
inline uoffset_t GetPrefixedSize(const uint8_t *buf) {
|
||||
return ReadScalar<uoffset_t>(buf);
|
||||
}
|
||||
|
||||
// Base class for native objects (FlatBuffer data de-serialized into native
|
||||
// C++ data structures).
|
||||
// Contains no functionality, purely documentative.
|
||||
struct NativeTable {};
|
||||
|
||||
/// @brief Function types to be used with resolving hashes into objects and
|
||||
/// back again. The resolver gets a pointer to a field inside an object API
|
||||
/// object that is of the type specified in the schema using the attribute
|
||||
/// `cpp_type` (it is thus important whatever you write to this address
|
||||
/// matches that type). The value of this field is initially null, so you
|
||||
/// may choose to implement a delayed binding lookup using this function
|
||||
/// if you wish. The resolver does the opposite lookup, for when the object
|
||||
/// is being serialized again.
|
||||
typedef uint64_t hash_value_t;
|
||||
typedef std::function<void(void **pointer_adr, hash_value_t hash)>
|
||||
resolver_function_t;
|
||||
typedef std::function<hash_value_t(void *pointer)> rehasher_function_t;
|
||||
|
||||
// Helper function to test if a field is present, using any of the field
|
||||
// enums in the generated code.
|
||||
// `table` must be a generated table type. Since this is a template parameter,
|
||||
// this is not typechecked to be a subclass of Table, so beware!
|
||||
// Note: this function will return false for fields equal to the default
|
||||
// value, since they're not stored in the buffer (unless force_defaults was
|
||||
// used).
|
||||
template<typename T>
|
||||
bool IsFieldPresent(const T *table, typename T::FlatBuffersVTableOffset field) {
|
||||
// Cast, since Table is a private baseclass of any table types.
|
||||
return reinterpret_cast<const Table *>(table)->CheckField(
|
||||
static_cast<voffset_t>(field));
|
||||
}
|
||||
|
||||
// Utility function for reverse lookups on the EnumNames*() functions
|
||||
// (in the generated C++ code)
|
||||
// names must be NULL terminated.
|
||||
inline int LookupEnum(const char **names, const char *name) {
|
||||
for (const char **p = names; *p; p++)
|
||||
if (!strcmp(*p, name)) return static_cast<int>(p - names);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// These macros allow us to layout a struct with a guarantee that they'll end
|
||||
// up looking the same on different compilers and platforms.
|
||||
// It does this by disallowing the compiler to do any padding, and then
|
||||
// does padding itself by inserting extra padding fields that make every
|
||||
// element aligned to its own size.
|
||||
// Additionally, it manually sets the alignment of the struct as a whole,
|
||||
// which is typically its largest element, or a custom size set in the schema
|
||||
// by the force_align attribute.
|
||||
// These are used in the generated code only.
|
||||
|
||||
// clang-format off
|
||||
#if defined(_MSC_VER)
|
||||
#define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \
|
||||
__pragma(pack(1)) \
|
||||
struct __declspec(align(alignment))
|
||||
#define FLATBUFFERS_STRUCT_END(name, size) \
|
||||
__pragma(pack()) \
|
||||
static_assert(sizeof(name) == size, "compiler breaks packing rules")
|
||||
#elif defined(__GNUC__) || defined(__clang__) || defined(__ICCARM__)
|
||||
#define FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(alignment) \
|
||||
_Pragma("pack(1)") \
|
||||
struct __attribute__((aligned(alignment)))
|
||||
#define FLATBUFFERS_STRUCT_END(name, size) \
|
||||
_Pragma("pack()") \
|
||||
static_assert(sizeof(name) == size, "compiler breaks packing rules")
|
||||
#else
|
||||
#error Unknown compiler, please define structure alignment macros
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// Minimal reflection via code generation.
|
||||
// Besides full-fat reflection (see reflection.h) and parsing/printing by
|
||||
// loading schemas (see idl.h), we can also have code generation for minimal
|
||||
// reflection data which allows pretty-printing and other uses without needing
|
||||
// a schema or a parser.
|
||||
// Generate code with --reflect-types (types only) or --reflect-names (names
|
||||
// also) to enable.
|
||||
// See minireflect.h for utilities using this functionality.
|
||||
|
||||
// These types are organized slightly differently as the ones in idl.h.
|
||||
enum SequenceType { ST_TABLE, ST_STRUCT, ST_UNION, ST_ENUM };
|
||||
|
||||
// Scalars have the same order as in idl.h
|
||||
// clang-format off
|
||||
#define FLATBUFFERS_GEN_ELEMENTARY_TYPES(ET) \
|
||||
ET(ET_UTYPE) \
|
||||
ET(ET_BOOL) \
|
||||
ET(ET_CHAR) \
|
||||
ET(ET_UCHAR) \
|
||||
ET(ET_SHORT) \
|
||||
ET(ET_USHORT) \
|
||||
ET(ET_INT) \
|
||||
ET(ET_UINT) \
|
||||
ET(ET_LONG) \
|
||||
ET(ET_ULONG) \
|
||||
ET(ET_FLOAT) \
|
||||
ET(ET_DOUBLE) \
|
||||
ET(ET_STRING) \
|
||||
ET(ET_SEQUENCE) // See SequenceType.
|
||||
|
||||
enum ElementaryType {
|
||||
#define FLATBUFFERS_ET(E) E,
|
||||
FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET)
|
||||
#undef FLATBUFFERS_ET
|
||||
};
|
||||
|
||||
inline const char * const *ElementaryTypeNames() {
|
||||
static const char * const names[] = {
|
||||
#define FLATBUFFERS_ET(E) #E,
|
||||
FLATBUFFERS_GEN_ELEMENTARY_TYPES(FLATBUFFERS_ET)
|
||||
#undef FLATBUFFERS_ET
|
||||
};
|
||||
return names;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
// Basic type info cost just 16bits per field!
|
||||
// We're explicitly defining the signedness since the signedness of integer
|
||||
// bitfields is otherwise implementation-defined and causes warnings on older
|
||||
// GCC compilers.
|
||||
struct TypeCode {
|
||||
// ElementaryType
|
||||
unsigned short base_type : 4;
|
||||
// Either vector (in table) or array (in struct)
|
||||
unsigned short is_repeating : 1;
|
||||
// Index into type_refs below, or -1 for none.
|
||||
signed short sequence_ref : 11;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TypeCode) == 2, "TypeCode");
|
||||
|
||||
struct TypeTable;
|
||||
|
||||
// Signature of the static method present in each type.
|
||||
typedef const TypeTable *(*TypeFunction)();
|
||||
|
||||
struct TypeTable {
|
||||
SequenceType st;
|
||||
size_t num_elems; // of type_codes, values, names (but not type_refs).
|
||||
const TypeCode *type_codes; // num_elems count
|
||||
const TypeFunction *type_refs; // less than num_elems entries (see TypeCode).
|
||||
const int16_t *array_sizes; // less than num_elems entries (see TypeCode).
|
||||
const int64_t *values; // Only set for non-consecutive enum/union or structs.
|
||||
const char *const *names; // Only set if compiled with --reflect-names.
|
||||
};
|
||||
|
||||
// String which identifies the current version of FlatBuffers.
|
||||
inline const char *flatbuffers_version_string() {
|
||||
return "FlatBuffers " FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "."
|
||||
FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "."
|
||||
FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define FLATBUFFERS_DEFINE_BITMASK_OPERATORS(E, T)\
|
||||
inline E operator | (E lhs, E rhs){\
|
||||
return E(T(lhs) | T(rhs));\
|
||||
}\
|
||||
inline E operator & (E lhs, E rhs){\
|
||||
return E(T(lhs) & T(rhs));\
|
||||
}\
|
||||
inline E operator ^ (E lhs, E rhs){\
|
||||
return E(T(lhs) ^ T(rhs));\
|
||||
}\
|
||||
inline E operator ~ (E lhs){\
|
||||
return E(~T(lhs));\
|
||||
}\
|
||||
inline E operator |= (E &lhs, E rhs){\
|
||||
lhs = lhs | rhs;\
|
||||
return lhs;\
|
||||
}\
|
||||
inline E operator &= (E &lhs, E rhs){\
|
||||
lhs = lhs & rhs;\
|
||||
return lhs;\
|
||||
}\
|
||||
inline E operator ^= (E &lhs, E rhs){\
|
||||
lhs = lhs ^ rhs;\
|
||||
return lhs;\
|
||||
}\
|
||||
inline bool operator !(E rhs) \
|
||||
{\
|
||||
return !bool(T(rhs)); \
|
||||
}
|
||||
/// @endcond
|
||||
} // namespace flatbuffers
|
||||
|
||||
// clang-format on
|
||||
|
||||
#endif // FLATBUFFERS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,509 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_STL_EMULATION_H_
|
||||
#define FLATBUFFERS_STL_EMULATION_H_
|
||||
|
||||
// clang-format off
|
||||
#include "flatbuffers/base.h"
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
#ifndef FLATBUFFERS_USE_STD_OPTIONAL
|
||||
// Detect C++17 compatible compiler.
|
||||
// __cplusplus >= 201703L - a compiler has support of 'static inline' variables.
|
||||
#if (defined(__cplusplus) && __cplusplus >= 201703L) \
|
||||
|| (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
#define FLATBUFFERS_USE_STD_OPTIONAL 1
|
||||
#else
|
||||
#define FLATBUFFERS_USE_STD_OPTIONAL 0
|
||||
#endif // (defined(__cplusplus) && __cplusplus >= 201703L) ...
|
||||
#endif // FLATBUFFERS_USE_STD_OPTIONAL
|
||||
|
||||
#if FLATBUFFERS_USE_STD_OPTIONAL
|
||||
#include <optional>
|
||||
#endif
|
||||
|
||||
// The __cpp_lib_span is the predefined feature macro.
|
||||
#if defined(FLATBUFFERS_USE_STD_SPAN)
|
||||
#include <span>
|
||||
#elif defined(__cpp_lib_span) && defined(__has_include)
|
||||
#if __has_include(<span>)
|
||||
#include <span>
|
||||
#define FLATBUFFERS_USE_STD_SPAN
|
||||
#endif
|
||||
#else
|
||||
// Disable non-trivial ctors if FLATBUFFERS_SPAN_MINIMAL defined.
|
||||
#if !defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
#define FLATBUFFERS_SPAN_MINIMAL
|
||||
#else
|
||||
// Enable implicit construction of a span<T,N> from a std::array<T,N>.
|
||||
#include <array>
|
||||
#endif
|
||||
#endif // defined(FLATBUFFERS_USE_STD_SPAN)
|
||||
|
||||
// This header provides backwards compatibility for older versions of the STL.
|
||||
namespace flatbuffers {
|
||||
|
||||
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
template <typename T>
|
||||
using numeric_limits = std::numeric_limits<T>;
|
||||
#else
|
||||
template <typename T> class numeric_limits :
|
||||
public std::numeric_limits<T> {};
|
||||
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
|
||||
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
template <typename T> using is_scalar = std::is_scalar<T>;
|
||||
template <typename T, typename U> using is_same = std::is_same<T,U>;
|
||||
template <typename T> using is_floating_point = std::is_floating_point<T>;
|
||||
template <typename T> using is_unsigned = std::is_unsigned<T>;
|
||||
template <typename T> using is_enum = std::is_enum<T>;
|
||||
template <typename T> using make_unsigned = std::make_unsigned<T>;
|
||||
template<bool B, class T, class F>
|
||||
using conditional = std::conditional<B, T, F>;
|
||||
template<class T, T v>
|
||||
using integral_constant = std::integral_constant<T, v>;
|
||||
template <bool B>
|
||||
using bool_constant = integral_constant<bool, B>;
|
||||
using true_type = std::true_type;
|
||||
using false_type = std::false_type;
|
||||
#else
|
||||
// MSVC 2010 doesn't support C++11 aliases.
|
||||
template <typename T> struct is_scalar : public std::is_scalar<T> {};
|
||||
template <typename T, typename U> struct is_same : public std::is_same<T,U> {};
|
||||
template <typename T> struct is_floating_point :
|
||||
public std::is_floating_point<T> {};
|
||||
template <typename T> struct is_unsigned : public std::is_unsigned<T> {};
|
||||
template <typename T> struct is_enum : public std::is_enum<T> {};
|
||||
template <typename T> struct make_unsigned : public std::make_unsigned<T> {};
|
||||
template<bool B, class T, class F>
|
||||
struct conditional : public std::conditional<B, T, F> {};
|
||||
template<class T, T v>
|
||||
struct integral_constant : public std::integral_constant<T, v> {};
|
||||
template <bool B>
|
||||
struct bool_constant : public integral_constant<bool, B> {};
|
||||
typedef bool_constant<true> true_type;
|
||||
typedef bool_constant<false> false_type;
|
||||
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
|
||||
#if defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
template <class T> using unique_ptr = std::unique_ptr<T>;
|
||||
#else
|
||||
// MSVC 2010 doesn't support C++11 aliases.
|
||||
// We're manually "aliasing" the class here as we want to bring unique_ptr
|
||||
// into the flatbuffers namespace. We have unique_ptr in the flatbuffers
|
||||
// namespace we have a completely independent implementation (see below)
|
||||
// for C++98 STL implementations.
|
||||
template <class T> class unique_ptr : public std::unique_ptr<T> {
|
||||
public:
|
||||
unique_ptr() {}
|
||||
explicit unique_ptr(T* p) : std::unique_ptr<T>(p) {}
|
||||
unique_ptr(std::unique_ptr<T>&& u) { *this = std::move(u); }
|
||||
unique_ptr(unique_ptr&& u) { *this = std::move(u); }
|
||||
unique_ptr& operator=(std::unique_ptr<T>&& u) {
|
||||
std::unique_ptr<T>::reset(u.release());
|
||||
return *this;
|
||||
}
|
||||
unique_ptr& operator=(unique_ptr&& u) {
|
||||
std::unique_ptr<T>::reset(u.release());
|
||||
return *this;
|
||||
}
|
||||
unique_ptr& operator=(T* p) {
|
||||
return std::unique_ptr<T>::operator=(p);
|
||||
}
|
||||
};
|
||||
#endif // defined(FLATBUFFERS_TEMPLATES_ALIASES)
|
||||
|
||||
#if FLATBUFFERS_USE_STD_OPTIONAL
|
||||
template<class T>
|
||||
using Optional = std::optional<T>;
|
||||
using nullopt_t = std::nullopt_t;
|
||||
inline constexpr nullopt_t nullopt = std::nullopt;
|
||||
|
||||
#else
|
||||
// Limited implementation of Optional<T> type for a scalar T.
|
||||
// This implementation limited by trivial types compatible with
|
||||
// std::is_arithmetic<T> or std::is_enum<T> type traits.
|
||||
|
||||
// A tag to indicate an empty flatbuffers::optional<T>.
|
||||
struct nullopt_t {
|
||||
explicit FLATBUFFERS_CONSTEXPR_CPP11 nullopt_t(int) {}
|
||||
};
|
||||
|
||||
#if defined(FLATBUFFERS_CONSTEXPR_DEFINED)
|
||||
namespace internal {
|
||||
template <class> struct nullopt_holder {
|
||||
static constexpr nullopt_t instance_ = nullopt_t(0);
|
||||
};
|
||||
template<class Dummy>
|
||||
constexpr nullopt_t nullopt_holder<Dummy>::instance_;
|
||||
}
|
||||
static constexpr const nullopt_t &nullopt = internal::nullopt_holder<void>::instance_;
|
||||
|
||||
#else
|
||||
namespace internal {
|
||||
template <class> struct nullopt_holder {
|
||||
static const nullopt_t instance_;
|
||||
};
|
||||
template<class Dummy>
|
||||
const nullopt_t nullopt_holder<Dummy>::instance_ = nullopt_t(0);
|
||||
}
|
||||
static const nullopt_t &nullopt = internal::nullopt_holder<void>::instance_;
|
||||
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
class Optional FLATBUFFERS_FINAL_CLASS {
|
||||
// Non-scalar 'T' would extremely complicated Optional<T>.
|
||||
// Use is_scalar<T> checking because flatbuffers flatbuffers::is_arithmetic<T>
|
||||
// isn't implemented.
|
||||
static_assert(flatbuffers::is_scalar<T>::value, "unexpected type T");
|
||||
|
||||
public:
|
||||
~Optional() {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 Optional() FLATBUFFERS_NOEXCEPT
|
||||
: value_(), has_value_(false) {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 Optional(nullopt_t) FLATBUFFERS_NOEXCEPT
|
||||
: value_(), has_value_(false) {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 Optional(T val) FLATBUFFERS_NOEXCEPT
|
||||
: value_(val), has_value_(true) {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 Optional(const Optional &other) FLATBUFFERS_NOEXCEPT
|
||||
: value_(other.value_), has_value_(other.has_value_) {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(const Optional &other) FLATBUFFERS_NOEXCEPT {
|
||||
value_ = other.value_;
|
||||
has_value_ = other.has_value_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(nullopt_t) FLATBUFFERS_NOEXCEPT {
|
||||
value_ = T();
|
||||
has_value_ = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP14 Optional &operator=(T val) FLATBUFFERS_NOEXCEPT {
|
||||
value_ = val;
|
||||
has_value_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() FLATBUFFERS_NOEXCEPT {
|
||||
*this = nullopt;
|
||||
}
|
||||
|
||||
void swap(Optional &other) FLATBUFFERS_NOEXCEPT {
|
||||
std::swap(value_, other.value_);
|
||||
std::swap(has_value_, other.has_value_);
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 FLATBUFFERS_EXPLICIT_CPP11 operator bool() const FLATBUFFERS_NOEXCEPT {
|
||||
return has_value_;
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool has_value() const FLATBUFFERS_NOEXCEPT {
|
||||
return has_value_;
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 const T& operator*() const FLATBUFFERS_NOEXCEPT {
|
||||
return value_;
|
||||
}
|
||||
|
||||
const T& value() const {
|
||||
FLATBUFFERS_ASSERT(has_value());
|
||||
return value_;
|
||||
}
|
||||
|
||||
T value_or(T default_value) const FLATBUFFERS_NOEXCEPT {
|
||||
return has_value() ? value_ : default_value;
|
||||
}
|
||||
|
||||
private:
|
||||
T value_;
|
||||
bool has_value_;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& opt, nullopt_t) FLATBUFFERS_NOEXCEPT {
|
||||
return !opt;
|
||||
}
|
||||
template<class T>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(nullopt_t, const Optional<T>& opt) FLATBUFFERS_NOEXCEPT {
|
||||
return !opt;
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& lhs, const U& rhs) FLATBUFFERS_NOEXCEPT {
|
||||
return static_cast<bool>(lhs) && (*lhs == rhs);
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const T& lhs, const Optional<U>& rhs) FLATBUFFERS_NOEXCEPT {
|
||||
return static_cast<bool>(rhs) && (lhs == *rhs);
|
||||
}
|
||||
|
||||
template<class T, class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) FLATBUFFERS_NOEXCEPT {
|
||||
return static_cast<bool>(lhs) != static_cast<bool>(rhs)
|
||||
? false
|
||||
: !static_cast<bool>(lhs) ? false : (*lhs == *rhs);
|
||||
}
|
||||
#endif // FLATBUFFERS_USE_STD_OPTIONAL
|
||||
|
||||
|
||||
// Very limited and naive partial implementation of C++20 std::span<T,Extent>.
|
||||
#if defined(FLATBUFFERS_USE_STD_SPAN)
|
||||
inline constexpr std::size_t dynamic_extent = std::dynamic_extent;
|
||||
template<class T, std::size_t Extent = std::dynamic_extent>
|
||||
using span = std::span<T, Extent>;
|
||||
|
||||
#else // !defined(FLATBUFFERS_USE_STD_SPAN)
|
||||
FLATBUFFERS_CONSTEXPR std::size_t dynamic_extent = static_cast<std::size_t>(-1);
|
||||
|
||||
// Exclude this code if MSVC2010 or non-STL Android is active.
|
||||
// The non-STL Android doesn't have `std::is_convertible` required for SFINAE.
|
||||
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
namespace internal {
|
||||
// This is SFINAE helper class for checking of a common condition:
|
||||
// > This overload only participates in overload resolution
|
||||
// > Check whether a pointer to an array of From can be converted
|
||||
// > to a pointer to an array of To.
|
||||
// This helper is used for checking of 'From -> const From'.
|
||||
template<class To, std::size_t Extent, class From, std::size_t N>
|
||||
struct is_span_convertable {
|
||||
using type =
|
||||
typename std::conditional<std::is_convertible<From (*)[], To (*)[]>::value
|
||||
&& (Extent == dynamic_extent || N == Extent),
|
||||
int, void>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct SpanIterator {
|
||||
// TODO: upgrade to std::random_access_iterator_tag.
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = typename std::remove_cv<T>::type;
|
||||
using reference = T&;
|
||||
using pointer = T*;
|
||||
|
||||
// Convince MSVC compiler that this iterator is trusted (it is verified).
|
||||
#ifdef _MSC_VER
|
||||
using _Unchecked_type = pointer;
|
||||
#endif // _MSC_VER
|
||||
|
||||
SpanIterator(pointer ptr) : ptr_(ptr) {}
|
||||
reference operator*() const { return *ptr_; }
|
||||
pointer operator->() { return ptr_; }
|
||||
SpanIterator& operator++() { ptr_++; return *this; }
|
||||
SpanIterator operator++(int) { auto tmp = *this; ++(*this); return tmp; }
|
||||
|
||||
friend bool operator== (const SpanIterator& lhs, const SpanIterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
|
||||
friend bool operator!= (const SpanIterator& lhs, const SpanIterator& rhs) { return lhs.ptr_ != rhs.ptr_; }
|
||||
|
||||
private:
|
||||
pointer ptr_;
|
||||
};
|
||||
} // namespace internal
|
||||
#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
|
||||
// T - element type; must be a complete type that is not an abstract
|
||||
// class type.
|
||||
// Extent - the number of elements in the sequence, or dynamic.
|
||||
template<class T, std::size_t Extent = dynamic_extent>
|
||||
class span FLATBUFFERS_FINAL_CLASS {
|
||||
public:
|
||||
typedef T element_type;
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
typedef std::size_t size_type;
|
||||
|
||||
static FLATBUFFERS_CONSTEXPR size_type extent = Extent;
|
||||
|
||||
// Returns the number of elements in the span.
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 size_type size() const FLATBUFFERS_NOEXCEPT {
|
||||
return count_;
|
||||
}
|
||||
|
||||
// Returns the size of the sequence in bytes.
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
size_type size_bytes() const FLATBUFFERS_NOEXCEPT {
|
||||
return size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
// Checks if the span is empty.
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 bool empty() const FLATBUFFERS_NOEXCEPT {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// Returns a pointer to the beginning of the sequence.
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 pointer data() const FLATBUFFERS_NOEXCEPT {
|
||||
return data_;
|
||||
}
|
||||
|
||||
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
using Iterator = internal::SpanIterator<T>;
|
||||
|
||||
Iterator begin() const { return Iterator(data()); }
|
||||
Iterator end() const { return Iterator(data() + size()); }
|
||||
#endif
|
||||
|
||||
// Returns a reference to the idx-th element of the sequence.
|
||||
// The behavior is undefined if the idx is greater than or equal to size().
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 reference operator[](size_type idx) const {
|
||||
return data()[idx];
|
||||
}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span(const span &other) FLATBUFFERS_NOEXCEPT
|
||||
: data_(other.data_), count_(other.count_) {}
|
||||
|
||||
FLATBUFFERS_CONSTEXPR_CPP14 span &operator=(const span &other)
|
||||
FLATBUFFERS_NOEXCEPT {
|
||||
data_ = other.data_;
|
||||
count_ = other.count_;
|
||||
}
|
||||
|
||||
// Limited implementation of
|
||||
// `template <class It> constexpr std::span(It first, size_type count);`.
|
||||
//
|
||||
// Constructs a span that is a view over the range [first, first + count);
|
||||
// the resulting span has: data() == first and size() == count.
|
||||
// The behavior is undefined if [first, first + count) is not a valid range,
|
||||
// or if (extent != flatbuffers::dynamic_extent && count != extent).
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
explicit span(pointer first, size_type count) FLATBUFFERS_NOEXCEPT
|
||||
: data_ (Extent == dynamic_extent ? first : (Extent == count ? first : nullptr)),
|
||||
count_(Extent == dynamic_extent ? count : (Extent == count ? Extent : 0)) {
|
||||
// Make span empty if the count argument is incompatible with span<T,N>.
|
||||
}
|
||||
|
||||
// Exclude this code if MSVC2010 is active. The MSVC2010 isn't C++11
|
||||
// compliant, it doesn't support default template arguments for functions.
|
||||
#if defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
|
||||
count_(0) {
|
||||
static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
|
||||
}
|
||||
|
||||
#else
|
||||
// Constructs an empty span whose data() == nullptr and size() == 0.
|
||||
// This overload only participates in overload resolution if
|
||||
// extent == 0 || extent == flatbuffers::dynamic_extent.
|
||||
// A dummy template argument N is need dependency for SFINAE.
|
||||
template<std::size_t N = 0,
|
||||
typename internal::is_span_convertable<element_type, Extent, element_type, (N - N)>::type = 0>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span() FLATBUFFERS_NOEXCEPT : data_(nullptr),
|
||||
count_(0) {
|
||||
static_assert(extent == 0 || extent == dynamic_extent, "invalid span");
|
||||
}
|
||||
|
||||
// Constructs a span that is a view over the array arr; the resulting span
|
||||
// has size() == N and data() == std::data(arr). These overloads only
|
||||
// participate in overload resolution if
|
||||
// extent == std::dynamic_extent || N == extent is true and
|
||||
// std::remove_pointer_t<decltype(std::data(arr))>(*)[]
|
||||
// is convertible to element_type (*)[].
|
||||
template<std::size_t N,
|
||||
typename internal::is_span_convertable<element_type, Extent, element_type, N>::type = 0>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span(element_type (&arr)[N]) FLATBUFFERS_NOEXCEPT
|
||||
: data_(arr), count_(N) {}
|
||||
|
||||
template<class U, std::size_t N,
|
||||
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
|
||||
: data_(arr.data()), count_(N) {}
|
||||
|
||||
//template<class U, std::size_t N,
|
||||
// int = 0>
|
||||
//FLATBUFFERS_CONSTEXPR_CPP11 span(std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
|
||||
// : data_(arr.data()), count_(N) {}
|
||||
|
||||
template<class U, std::size_t N,
|
||||
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span(const std::array<U, N> &arr) FLATBUFFERS_NOEXCEPT
|
||||
: data_(arr.data()), count_(N) {}
|
||||
|
||||
// Converting constructor from another span s;
|
||||
// the resulting span has size() == s.size() and data() == s.data().
|
||||
// This overload only participates in overload resolution
|
||||
// if extent == std::dynamic_extent || N == extent is true and U (*)[]
|
||||
// is convertible to element_type (*)[].
|
||||
template<class U, std::size_t N,
|
||||
typename internal::is_span_convertable<element_type, Extent, U, N>::type = 0>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 span(const flatbuffers::span<U, N> &s) FLATBUFFERS_NOEXCEPT
|
||||
: span(s.data(), s.size()) {
|
||||
}
|
||||
|
||||
#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
|
||||
private:
|
||||
// This is a naive implementation with 'count_' member even if (Extent != dynamic_extent).
|
||||
pointer const data_;
|
||||
size_type count_;
|
||||
};
|
||||
#endif // defined(FLATBUFFERS_USE_STD_SPAN)
|
||||
|
||||
#if !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<ElementType, Extent> make_span(ElementType(&arr)[Extent]) FLATBUFFERS_NOEXCEPT {
|
||||
return span<ElementType, Extent>(arr);
|
||||
}
|
||||
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<const ElementType, Extent> make_span(const ElementType(&arr)[Extent]) FLATBUFFERS_NOEXCEPT {
|
||||
return span<const ElementType, Extent>(arr);
|
||||
}
|
||||
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<ElementType, Extent> make_span(std::array<ElementType, Extent> &arr) FLATBUFFERS_NOEXCEPT {
|
||||
return span<ElementType, Extent>(arr);
|
||||
}
|
||||
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<const ElementType, Extent> make_span(const std::array<ElementType, Extent> &arr) FLATBUFFERS_NOEXCEPT {
|
||||
return span<const ElementType, Extent>(arr);
|
||||
}
|
||||
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<ElementType, dynamic_extent> make_span(ElementType *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
|
||||
return span<ElementType, dynamic_extent>(first, count);
|
||||
}
|
||||
|
||||
template<class ElementType, std::size_t Extent>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11
|
||||
flatbuffers::span<const ElementType, dynamic_extent> make_span(const ElementType *first, std::size_t count) FLATBUFFERS_NOEXCEPT {
|
||||
return span<const ElementType, dynamic_extent>(first, count);
|
||||
}
|
||||
#endif // !defined(FLATBUFFERS_SPAN_MINIMAL)
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_STL_EMULATION_H_
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_TABLE_H_
|
||||
#define FLATBUFFERS_TABLE_H_
|
||||
|
||||
#include "flatbuffers/base.h"
|
||||
#include "flatbuffers/verifier.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
// "tables" use an offset table (possibly shared) that allows fields to be
|
||||
// omitted and added at will, but uses an extra indirection to read.
|
||||
class Table {
|
||||
public:
|
||||
const uint8_t *GetVTable() const {
|
||||
return data_ - ReadScalar<soffset_t>(data_);
|
||||
}
|
||||
|
||||
// This gets the field offset for any of the functions below it, or 0
|
||||
// if the field was not present.
|
||||
voffset_t GetOptionalFieldOffset(voffset_t field) const {
|
||||
// The vtable offset is always at the start.
|
||||
auto vtable = GetVTable();
|
||||
// The first element is the size of the vtable (fields + type id + itself).
|
||||
auto vtsize = ReadScalar<voffset_t>(vtable);
|
||||
// If the field we're accessing is outside the vtable, we're reading older
|
||||
// data, so it's the same as if the offset was 0 (not present).
|
||||
return field < vtsize ? ReadScalar<voffset_t>(vtable + field) : 0;
|
||||
}
|
||||
|
||||
template<typename T> T GetField(voffset_t field, T defaultval) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
return field_offset ? ReadScalar<T>(data_ + field_offset) : defaultval;
|
||||
}
|
||||
|
||||
template<typename P> P GetPointer(voffset_t field) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
auto p = data_ + field_offset;
|
||||
return field_offset ? reinterpret_cast<P>(p + ReadScalar<uoffset_t>(p))
|
||||
: nullptr;
|
||||
}
|
||||
template<typename P> P GetPointer(voffset_t field) const {
|
||||
return const_cast<Table *>(this)->GetPointer<P>(field);
|
||||
}
|
||||
|
||||
template<typename P> P GetStruct(voffset_t field) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
auto p = const_cast<uint8_t *>(data_ + field_offset);
|
||||
return field_offset ? reinterpret_cast<P>(p) : nullptr;
|
||||
}
|
||||
|
||||
template<typename Raw, typename Face>
|
||||
flatbuffers::Optional<Face> GetOptional(voffset_t field) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
auto p = data_ + field_offset;
|
||||
return field_offset ? Optional<Face>(static_cast<Face>(ReadScalar<Raw>(p)))
|
||||
: Optional<Face>();
|
||||
}
|
||||
|
||||
template<typename T> bool SetField(voffset_t field, T val, T def) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
if (!field_offset) return IsTheSameAs(val, def);
|
||||
WriteScalar(data_ + field_offset, val);
|
||||
return true;
|
||||
}
|
||||
template<typename T> bool SetField(voffset_t field, T val) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
if (!field_offset) return false;
|
||||
WriteScalar(data_ + field_offset, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPointer(voffset_t field, const uint8_t *val) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
if (!field_offset) return false;
|
||||
WriteScalar(data_ + field_offset,
|
||||
static_cast<uoffset_t>(val - (data_ + field_offset)));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *GetAddressOf(voffset_t field) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
return field_offset ? data_ + field_offset : nullptr;
|
||||
}
|
||||
const uint8_t *GetAddressOf(voffset_t field) const {
|
||||
return const_cast<Table *>(this)->GetAddressOf(field);
|
||||
}
|
||||
|
||||
bool CheckField(voffset_t field) const {
|
||||
return GetOptionalFieldOffset(field) != 0;
|
||||
}
|
||||
|
||||
// Verify the vtable of this table.
|
||||
// Call this once per table, followed by VerifyField once per field.
|
||||
bool VerifyTableStart(Verifier &verifier) const {
|
||||
return verifier.VerifyTableStart(data_);
|
||||
}
|
||||
|
||||
// Verify a particular field.
|
||||
template<typename T>
|
||||
bool VerifyField(const Verifier &verifier, voffset_t field,
|
||||
size_t align) const {
|
||||
// Calling GetOptionalFieldOffset should be safe now thanks to
|
||||
// VerifyTable().
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
// Check the actual field.
|
||||
return !field_offset || verifier.VerifyField<T>(data_, field_offset, align);
|
||||
}
|
||||
|
||||
// VerifyField for required fields.
|
||||
template<typename T>
|
||||
bool VerifyFieldRequired(const Verifier &verifier, voffset_t field,
|
||||
size_t align) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
return verifier.Check(field_offset != 0) &&
|
||||
verifier.VerifyField<T>(data_, field_offset, align);
|
||||
}
|
||||
|
||||
// Versions for offsets.
|
||||
bool VerifyOffset(const Verifier &verifier, voffset_t field) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
return !field_offset || verifier.VerifyOffset(data_, field_offset);
|
||||
}
|
||||
|
||||
bool VerifyOffsetRequired(const Verifier &verifier, voffset_t field) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
return verifier.Check(field_offset != 0) &&
|
||||
verifier.VerifyOffset(data_, field_offset);
|
||||
}
|
||||
|
||||
private:
|
||||
// private constructor & copy constructor: you obtain instances of this
|
||||
// class by pointing to existing data only
|
||||
Table();
|
||||
Table(const Table &other);
|
||||
Table &operator=(const Table &);
|
||||
|
||||
uint8_t data_[1];
|
||||
};
|
||||
|
||||
// This specialization allows avoiding warnings like:
|
||||
// MSVC C4800: type: forcing value to bool 'true' or 'false'.
|
||||
template<>
|
||||
inline flatbuffers::Optional<bool> Table::GetOptional<uint8_t, bool>(
|
||||
voffset_t field) const {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
auto p = data_ + field_offset;
|
||||
return field_offset ? Optional<bool>(ReadScalar<uint8_t>(p) != 0)
|
||||
: Optional<bool>();
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_TABLE_H_
|
||||
@@ -1,725 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_UTIL_H_
|
||||
#define FLATBUFFERS_UTIL_H_
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "flatbuffers/base.h"
|
||||
#include "flatbuffers/stl_emulation.h"
|
||||
|
||||
// For TFLM we always want to use FLATBUFFERS_PREFER_PRINTF=1. See
|
||||
// http://b/211811553 for more context.
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
#define FLATBUFFERS_PREFER_PRINTF 1
|
||||
#endif
|
||||
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
# include <iomanip>
|
||||
# include <sstream>
|
||||
#else // FLATBUFFERS_PREFER_PRINTF
|
||||
# include <float.h>
|
||||
# include <stdio.h>
|
||||
#endif // FLATBUFFERS_PREFER_PRINTF
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
// @locale-independent functions for ASCII characters set.
|
||||
|
||||
// Fast checking that character lies in closed range: [a <= x <= b]
|
||||
// using one compare (conditional branch) operator.
|
||||
inline bool check_ascii_range(char x, char a, char b) {
|
||||
FLATBUFFERS_ASSERT(a <= b);
|
||||
// (Hacker's Delight): `a <= x <= b` <=> `(x-a) <={u} (b-a)`.
|
||||
// The x, a, b will be promoted to int and subtracted without overflow.
|
||||
return static_cast<unsigned int>(x - a) <= static_cast<unsigned int>(b - a);
|
||||
}
|
||||
|
||||
// Case-insensitive isalpha
|
||||
inline bool is_alpha(char c) {
|
||||
// ASCII only: alpha to upper case => reset bit 0x20 (~0x20 = 0xDF).
|
||||
return check_ascii_range(c & 0xDF, 'a' & 0xDF, 'z' & 0xDF);
|
||||
}
|
||||
|
||||
// Check for uppercase alpha
|
||||
inline bool is_alpha_upper(char c) { return check_ascii_range(c, 'A', 'Z'); }
|
||||
|
||||
// Check (case-insensitive) that `c` is equal to alpha.
|
||||
inline bool is_alpha_char(char c, char alpha) {
|
||||
FLATBUFFERS_ASSERT(is_alpha(alpha));
|
||||
// ASCII only: alpha to upper case => reset bit 0x20 (~0x20 = 0xDF).
|
||||
return ((c & 0xDF) == (alpha & 0xDF));
|
||||
}
|
||||
|
||||
// https://en.cppreference.com/w/cpp/string/byte/isxdigit
|
||||
// isdigit and isxdigit are the only standard narrow character classification
|
||||
// functions that are not affected by the currently installed C locale. although
|
||||
// some implementations (e.g. Microsoft in 1252 codepage) may classify
|
||||
// additional single-byte characters as digits.
|
||||
inline bool is_digit(char c) { return check_ascii_range(c, '0', '9'); }
|
||||
|
||||
inline bool is_xdigit(char c) {
|
||||
// Replace by look-up table.
|
||||
return is_digit(c) || check_ascii_range(c & 0xDF, 'a' & 0xDF, 'f' & 0xDF);
|
||||
}
|
||||
|
||||
// Case-insensitive isalnum
|
||||
inline bool is_alnum(char c) { return is_alpha(c) || is_digit(c); }
|
||||
|
||||
inline char CharToUpper(char c) {
|
||||
return static_cast<char>(::toupper(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
inline char CharToLower(char c) {
|
||||
return static_cast<char>(::tolower(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
// @end-locale-independent functions for ASCII character set
|
||||
|
||||
#ifdef FLATBUFFERS_PREFER_PRINTF
|
||||
template<typename T> size_t IntToDigitCount(T t) {
|
||||
size_t digit_count = 0;
|
||||
// Count the sign for negative numbers
|
||||
if (t < 0) digit_count++;
|
||||
// Count a single 0 left of the dot for fractional numbers
|
||||
if (-1 < t && t < 1) digit_count++;
|
||||
// Count digits until fractional part
|
||||
T eps = std::numeric_limits<T>::epsilon();
|
||||
while (t <= (-1 + eps) || (1 - eps) <= t) {
|
||||
t /= 10;
|
||||
digit_count++;
|
||||
}
|
||||
return digit_count;
|
||||
}
|
||||
|
||||
template<typename T> size_t NumToStringWidth(T t, int precision = 0) {
|
||||
size_t string_width = IntToDigitCount(t);
|
||||
// Count the dot for floating point numbers
|
||||
if (precision) string_width += (precision + 1);
|
||||
return string_width;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string NumToStringImplWrapper(T t, const char *fmt, int precision = 0) {
|
||||
size_t string_width = NumToStringWidth(t, precision);
|
||||
std::string s(string_width, 0x00);
|
||||
// Allow snprintf to use std::string trailing null to detect buffer overflow
|
||||
snprintf(const_cast<char *>(s.data()), (s.size() + 1), fmt, string_width, t);
|
||||
return s;
|
||||
}
|
||||
#endif // FLATBUFFERS_PREFER_PRINTF
|
||||
|
||||
// Convert an integer or floating point value to a string.
|
||||
// In contrast to std::stringstream, "char" values are
|
||||
// converted to a string of digits, and we don't use scientific notation.
|
||||
template<typename T> std::string NumToString(T t) {
|
||||
// clang-format off
|
||||
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
std::stringstream ss;
|
||||
ss << t;
|
||||
return ss.str();
|
||||
#else // FLATBUFFERS_PREFER_PRINTF
|
||||
auto v = static_cast<long long>(t);
|
||||
return NumToStringImplWrapper(v, "%.*lld");
|
||||
#endif // FLATBUFFERS_PREFER_PRINTF
|
||||
// clang-format on
|
||||
}
|
||||
// Avoid char types used as character data.
|
||||
template<> inline std::string NumToString<signed char>(signed char t) {
|
||||
return NumToString(static_cast<int>(t));
|
||||
}
|
||||
template<> inline std::string NumToString<unsigned char>(unsigned char t) {
|
||||
return NumToString(static_cast<int>(t));
|
||||
}
|
||||
template<> inline std::string NumToString<char>(char t) {
|
||||
return NumToString(static_cast<int>(t));
|
||||
}
|
||||
|
||||
// Special versions for floats/doubles.
|
||||
template<typename T> std::string FloatToString(T t, int precision) {
|
||||
// clang-format off
|
||||
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
// to_string() prints different numbers of digits for floats depending on
|
||||
// platform and isn't available on Android, so we use stringstream
|
||||
std::stringstream ss;
|
||||
// Use std::fixed to suppress scientific notation.
|
||||
ss << std::fixed;
|
||||
// Default precision is 6, we want that to be higher for doubles.
|
||||
ss << std::setprecision(precision);
|
||||
ss << t;
|
||||
auto s = ss.str();
|
||||
#else // FLATBUFFERS_PREFER_PRINTF
|
||||
auto v = static_cast<double>(t);
|
||||
auto s = NumToStringImplWrapper(v, "%0.*f", precision);
|
||||
#endif // FLATBUFFERS_PREFER_PRINTF
|
||||
// clang-format on
|
||||
// Sadly, std::fixed turns "1" into "1.00000", so here we undo that.
|
||||
auto p = s.find_last_not_of('0');
|
||||
if (p != std::string::npos) {
|
||||
// Strip trailing zeroes. If it is a whole number, keep one zero.
|
||||
s.resize(p + (s[p] == '.' ? 2 : 1));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
template<> inline std::string NumToString<double>(double t) {
|
||||
return FloatToString(t, 12);
|
||||
}
|
||||
template<> inline std::string NumToString<float>(float t) {
|
||||
return FloatToString(t, 6);
|
||||
}
|
||||
|
||||
// Convert an integer value to a hexadecimal string.
|
||||
// The returned string length is always xdigits long, prefixed by 0 digits.
|
||||
// For example, IntToStringHex(0x23, 8) returns the string "00000023".
|
||||
inline std::string IntToStringHex(int i, int xdigits) {
|
||||
FLATBUFFERS_ASSERT(i >= 0);
|
||||
// clang-format off
|
||||
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
std::stringstream ss;
|
||||
ss << std::setw(xdigits) << std::setfill('0') << std::hex << std::uppercase
|
||||
<< i;
|
||||
return ss.str();
|
||||
#else // FLATBUFFERS_PREFER_PRINTF
|
||||
return NumToStringImplWrapper(i, "%.*X", xdigits);
|
||||
#endif // FLATBUFFERS_PREFER_PRINTF
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
// Use locale independent functions {strtod_l, strtof_l, strtoll_l, strtoull_l}.
|
||||
#if defined(FLATBUFFERS_LOCALE_INDEPENDENT) && (FLATBUFFERS_LOCALE_INDEPENDENT > 0)
|
||||
class ClassicLocale {
|
||||
#ifdef _MSC_VER
|
||||
typedef _locale_t locale_type;
|
||||
#else
|
||||
typedef locale_t locale_type; // POSIX.1-2008 locale_t type
|
||||
#endif
|
||||
ClassicLocale();
|
||||
~ClassicLocale();
|
||||
locale_type locale_;
|
||||
static ClassicLocale instance_;
|
||||
public:
|
||||
static locale_type Get() { return instance_.locale_; }
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define __strtoull_impl(s, pe, b) _strtoui64_l(s, pe, b, ClassicLocale::Get())
|
||||
#define __strtoll_impl(s, pe, b) _strtoi64_l(s, pe, b, ClassicLocale::Get())
|
||||
#define __strtod_impl(s, pe) _strtod_l(s, pe, ClassicLocale::Get())
|
||||
#define __strtof_impl(s, pe) _strtof_l(s, pe, ClassicLocale::Get())
|
||||
#else
|
||||
#define __strtoull_impl(s, pe, b) strtoull_l(s, pe, b, ClassicLocale::Get())
|
||||
#define __strtoll_impl(s, pe, b) strtoll_l(s, pe, b, ClassicLocale::Get())
|
||||
#define __strtod_impl(s, pe) strtod_l(s, pe, ClassicLocale::Get())
|
||||
#define __strtof_impl(s, pe) strtof_l(s, pe, ClassicLocale::Get())
|
||||
#endif
|
||||
#else
|
||||
#define __strtod_impl(s, pe) strtod(s, pe)
|
||||
#define __strtof_impl(s, pe) static_cast<float>(strtod(s, pe))
|
||||
#ifdef _MSC_VER
|
||||
#define __strtoull_impl(s, pe, b) _strtoui64(s, pe, b)
|
||||
#define __strtoll_impl(s, pe, b) _strtoi64(s, pe, b)
|
||||
#else
|
||||
#define __strtoull_impl(s, pe, b) strtoull(s, pe, b)
|
||||
#define __strtoll_impl(s, pe, b) strtoll(s, pe, b)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
inline void strtoval_impl(int64_t *val, const char *str, char **endptr,
|
||||
int base) {
|
||||
*val = __strtoll_impl(str, endptr, base);
|
||||
}
|
||||
|
||||
inline void strtoval_impl(uint64_t *val, const char *str, char **endptr,
|
||||
int base) {
|
||||
*val = __strtoull_impl(str, endptr, base);
|
||||
}
|
||||
|
||||
inline void strtoval_impl(double *val, const char *str, char **endptr) {
|
||||
*val = __strtod_impl(str, endptr);
|
||||
}
|
||||
|
||||
// UBSAN: double to float is safe if numeric_limits<float>::is_iec559 is true.
|
||||
__supress_ubsan__("float-cast-overflow")
|
||||
inline void strtoval_impl(float *val, const char *str, char **endptr) {
|
||||
*val = __strtof_impl(str, endptr);
|
||||
}
|
||||
#undef __strtoull_impl
|
||||
#undef __strtoll_impl
|
||||
#undef __strtod_impl
|
||||
#undef __strtof_impl
|
||||
// clang-format on
|
||||
|
||||
// Adaptor for strtoull()/strtoll().
|
||||
// Flatbuffers accepts numbers with any count of leading zeros (-009 is -9),
|
||||
// while strtoll with base=0 interprets first leading zero as octal prefix.
|
||||
// In future, it is possible to add prefixed 0b0101.
|
||||
// 1) Checks errno code for overflow condition (out of range).
|
||||
// 2) If base <= 0, function try to detect base of number by prefix.
|
||||
//
|
||||
// Return value (like strtoull and strtoll, but reject partial result):
|
||||
// - If successful, an integer value corresponding to the str is returned.
|
||||
// - If full string conversion can't be performed, 0 is returned.
|
||||
// - If the converted value falls out of range of corresponding return type, a
|
||||
// range error occurs. In this case value MAX(T)/MIN(T) is returned.
|
||||
template<typename T>
|
||||
inline bool StringToIntegerImpl(T *val, const char *const str,
|
||||
const int base = 0,
|
||||
const bool check_errno = true) {
|
||||
// T is int64_t or uint64_T
|
||||
FLATBUFFERS_ASSERT(str);
|
||||
if (base <= 0) {
|
||||
auto s = str;
|
||||
while (*s && !is_digit(*s)) s++;
|
||||
if (s[0] == '0' && is_alpha_char(s[1], 'X'))
|
||||
return StringToIntegerImpl(val, str, 16, check_errno);
|
||||
// if a prefix not match, try base=10
|
||||
return StringToIntegerImpl(val, str, 10, check_errno);
|
||||
} else {
|
||||
if (check_errno) errno = 0; // clear thread-local errno
|
||||
auto endptr = str;
|
||||
strtoval_impl(val, str, const_cast<char **>(&endptr), base);
|
||||
if ((*endptr != '\0') || (endptr == str)) {
|
||||
*val = 0; // erase partial result
|
||||
return false; // invalid string
|
||||
}
|
||||
// errno is out-of-range, return MAX/MIN
|
||||
if (check_errno && errno) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool StringToFloatImpl(T *val, const char *const str) {
|
||||
// Type T must be either float or double.
|
||||
FLATBUFFERS_ASSERT(str && val);
|
||||
auto end = str;
|
||||
strtoval_impl(val, str, const_cast<char **>(&end));
|
||||
auto done = (end != str) && (*end == '\0');
|
||||
if (!done) *val = 0; // erase partial result
|
||||
return done;
|
||||
}
|
||||
|
||||
// Convert a string to an instance of T.
|
||||
// Return value (matched with StringToInteger64Impl and strtod):
|
||||
// - If successful, a numeric value corresponding to the str is returned.
|
||||
// - If full string conversion can't be performed, 0 is returned.
|
||||
// - If the converted value falls out of range of corresponding return type, a
|
||||
// range error occurs. In this case value MAX(T)/MIN(T) is returned.
|
||||
template<typename T> inline bool StringToNumber(const char *s, T *val) {
|
||||
// Assert on `unsigned long` and `signed long` on LP64.
|
||||
// If it is necessary, it could be solved with flatbuffers::enable_if<B,T>.
|
||||
static_assert(sizeof(T) < sizeof(int64_t), "unexpected type T");
|
||||
FLATBUFFERS_ASSERT(s && val);
|
||||
int64_t i64;
|
||||
// The errno check isn't needed, will return MAX/MIN on overflow.
|
||||
if (StringToIntegerImpl(&i64, s, 0, false)) {
|
||||
const int64_t max = (flatbuffers::numeric_limits<T>::max)();
|
||||
const int64_t min = flatbuffers::numeric_limits<T>::lowest();
|
||||
if (i64 > max) {
|
||||
*val = static_cast<T>(max);
|
||||
return false;
|
||||
}
|
||||
if (i64 < min) {
|
||||
// For unsigned types return max to distinguish from
|
||||
// "no conversion can be performed" when 0 is returned.
|
||||
*val = static_cast<T>(flatbuffers::is_unsigned<T>::value ? max : min);
|
||||
return false;
|
||||
}
|
||||
*val = static_cast<T>(i64);
|
||||
return true;
|
||||
}
|
||||
*val = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<> inline bool StringToNumber<int64_t>(const char *str, int64_t *val) {
|
||||
return StringToIntegerImpl(val, str);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool StringToNumber<uint64_t>(const char *str, uint64_t *val) {
|
||||
if (!StringToIntegerImpl(val, str)) return false;
|
||||
// The strtoull accepts negative numbers:
|
||||
// If the minus sign was part of the input sequence, the numeric value
|
||||
// calculated from the sequence of digits is negated as if by unary minus
|
||||
// in the result type, which applies unsigned integer wraparound rules.
|
||||
// Fix this behaviour (except -0).
|
||||
if (*val) {
|
||||
auto s = str;
|
||||
while (*s && !is_digit(*s)) s++;
|
||||
s = (s > str) ? (s - 1) : s; // step back to one symbol
|
||||
if (*s == '-') {
|
||||
// For unsigned types return the max to distinguish from
|
||||
// "no conversion can be performed".
|
||||
*val = (flatbuffers::numeric_limits<uint64_t>::max)();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> inline bool StringToNumber(const char *s, float *val) {
|
||||
return StringToFloatImpl(val, s);
|
||||
}
|
||||
|
||||
template<> inline bool StringToNumber(const char *s, double *val) {
|
||||
return StringToFloatImpl(val, s);
|
||||
}
|
||||
|
||||
inline int64_t StringToInt(const char *s, int base = 10) {
|
||||
int64_t val;
|
||||
return StringToIntegerImpl(&val, s, base) ? val : 0;
|
||||
}
|
||||
|
||||
inline uint64_t StringToUInt(const char *s, int base = 10) {
|
||||
uint64_t val;
|
||||
return StringToIntegerImpl(&val, s, base) ? val : 0;
|
||||
}
|
||||
|
||||
typedef bool (*LoadFileFunction)(const char *filename, bool binary,
|
||||
std::string *dest);
|
||||
typedef bool (*FileExistsFunction)(const char *filename);
|
||||
|
||||
LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function);
|
||||
|
||||
FileExistsFunction SetFileExistsFunction(
|
||||
FileExistsFunction file_exists_function);
|
||||
|
||||
// Check if file "name" exists.
|
||||
bool FileExists(const char *name);
|
||||
|
||||
// Check if "name" exists and it is also a directory.
|
||||
bool DirExists(const char *name);
|
||||
|
||||
// Load file "name" into "buf" returning true if successful
|
||||
// false otherwise. If "binary" is false data is read
|
||||
// using ifstream's text mode, otherwise data is read with
|
||||
// no transcoding.
|
||||
bool LoadFile(const char *name, bool binary, std::string *buf);
|
||||
|
||||
// Save data "buf" of length "len" bytes into a file
|
||||
// "name" returning true if successful, false otherwise.
|
||||
// If "binary" is false data is written using ifstream's
|
||||
// text mode, otherwise data is written with no
|
||||
// transcoding.
|
||||
bool SaveFile(const char *name, const char *buf, size_t len, bool binary);
|
||||
|
||||
// Save data "buf" into file "name" returning true if
|
||||
// successful, false otherwise. If "binary" is false
|
||||
// data is written using ifstream's text mode, otherwise
|
||||
// data is written with no transcoding.
|
||||
inline bool SaveFile(const char *name, const std::string &buf, bool binary) {
|
||||
return SaveFile(name, buf.c_str(), buf.size(), binary);
|
||||
}
|
||||
|
||||
// Functionality for minimalistic portable path handling.
|
||||
|
||||
// The functions below behave correctly regardless of whether posix ('/') or
|
||||
// Windows ('/' or '\\') separators are used.
|
||||
|
||||
// Any new separators inserted are always posix.
|
||||
FLATBUFFERS_CONSTEXPR char kPathSeparator = '/';
|
||||
|
||||
// Returns the path with the extension, if any, removed.
|
||||
std::string StripExtension(const std::string &filepath);
|
||||
|
||||
// Returns the extension, if any.
|
||||
std::string GetExtension(const std::string &filepath);
|
||||
|
||||
// Return the last component of the path, after the last separator.
|
||||
std::string StripPath(const std::string &filepath);
|
||||
|
||||
// Strip the last component of the path + separator.
|
||||
std::string StripFileName(const std::string &filepath);
|
||||
|
||||
std::string StripPrefix(const std::string &filepath,
|
||||
const std::string &prefix_to_remove);
|
||||
|
||||
// Concatenates a path with a filename, regardless of whether the path
|
||||
// ends in a separator or not.
|
||||
std::string ConCatPathFileName(const std::string &path,
|
||||
const std::string &filename);
|
||||
|
||||
// Replaces any '\\' separators with '/'
|
||||
std::string PosixPath(const char *path);
|
||||
std::string PosixPath(const std::string &path);
|
||||
|
||||
// This function ensure a directory exists, by recursively
|
||||
// creating dirs for any parts of the path that don't exist yet.
|
||||
void EnsureDirExists(const std::string &filepath);
|
||||
|
||||
// Obtains the absolute path from any other path.
|
||||
// Returns the input path if the absolute path couldn't be resolved.
|
||||
std::string AbsolutePath(const std::string &filepath);
|
||||
|
||||
// Returns files relative to the --project_root path, prefixed with `//`.
|
||||
std::string RelativeToRootPath(const std::string &project,
|
||||
const std::string &filepath);
|
||||
|
||||
// To and from UTF-8 unicode conversion functions
|
||||
|
||||
// Convert a unicode code point into a UTF-8 representation by appending it
|
||||
// to a string. Returns the number of bytes generated.
|
||||
inline int ToUTF8(uint32_t ucc, std::string *out) {
|
||||
FLATBUFFERS_ASSERT(!(ucc & 0x80000000)); // Top bit can't be set.
|
||||
// 6 possible encodings: http://en.wikipedia.org/wiki/UTF-8
|
||||
for (int i = 0; i < 6; i++) {
|
||||
// Max bits this encoding can represent.
|
||||
uint32_t max_bits = 6 + i * 5 + static_cast<int>(!i);
|
||||
if (ucc < (1u << max_bits)) { // does it fit?
|
||||
// Remaining bits not encoded in the first byte, store 6 bits each
|
||||
uint32_t remain_bits = i * 6;
|
||||
// Store first byte:
|
||||
(*out) += static_cast<char>((0xFE << (max_bits - remain_bits)) |
|
||||
(ucc >> remain_bits));
|
||||
// Store remaining bytes:
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
(*out) += static_cast<char>(((ucc >> (j * 6)) & 0x3F) | 0x80);
|
||||
}
|
||||
return i + 1; // Return the number of bytes added.
|
||||
}
|
||||
}
|
||||
FLATBUFFERS_ASSERT(0); // Impossible to arrive here.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Converts whatever prefix of the incoming string corresponds to a valid
|
||||
// UTF-8 sequence into a unicode code. The incoming pointer will have been
|
||||
// advanced past all bytes parsed.
|
||||
// returns -1 upon corrupt UTF-8 encoding (ignore the incoming pointer in
|
||||
// this case).
|
||||
inline int FromUTF8(const char **in) {
|
||||
int len = 0;
|
||||
// Count leading 1 bits.
|
||||
for (int mask = 0x80; mask >= 0x04; mask >>= 1) {
|
||||
if (**in & mask) {
|
||||
len++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((static_cast<unsigned char>(**in) << len) & 0x80)
|
||||
return -1; // Bit after leading 1's must be 0.
|
||||
if (!len) return *(*in)++;
|
||||
// UTF-8 encoded values with a length are between 2 and 4 bytes.
|
||||
if (len < 2 || len > 4) { return -1; }
|
||||
// Grab initial bits of the code.
|
||||
int ucc = *(*in)++ & ((1 << (7 - len)) - 1);
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
if ((**in & 0xC0) != 0x80) return -1; // Upper bits must 1 0.
|
||||
ucc <<= 6;
|
||||
ucc |= *(*in)++ & 0x3F; // Grab 6 more bits of the code.
|
||||
}
|
||||
// UTF-8 cannot encode values between 0xD800 and 0xDFFF (reserved for
|
||||
// UTF-16 surrogate pairs).
|
||||
if (ucc >= 0xD800 && ucc <= 0xDFFF) { return -1; }
|
||||
// UTF-8 must represent code points in their shortest possible encoding.
|
||||
switch (len) {
|
||||
case 2:
|
||||
// Two bytes of UTF-8 can represent code points from U+0080 to U+07FF.
|
||||
if (ucc < 0x0080 || ucc > 0x07FF) { return -1; }
|
||||
break;
|
||||
case 3:
|
||||
// Three bytes of UTF-8 can represent code points from U+0800 to U+FFFF.
|
||||
if (ucc < 0x0800 || ucc > 0xFFFF) { return -1; }
|
||||
break;
|
||||
case 4:
|
||||
// Four bytes of UTF-8 can represent code points from U+10000 to U+10FFFF.
|
||||
if (ucc < 0x10000 || ucc > 0x10FFFF) { return -1; }
|
||||
break;
|
||||
}
|
||||
return ucc;
|
||||
}
|
||||
|
||||
#ifndef FLATBUFFERS_PREFER_PRINTF
|
||||
// Wraps a string to a maximum length, inserting new lines where necessary. Any
|
||||
// existing whitespace will be collapsed down to a single space. A prefix or
|
||||
// suffix can be provided, which will be inserted before or after a wrapped
|
||||
// line, respectively.
|
||||
inline std::string WordWrap(const std::string in, size_t max_length,
|
||||
const std::string wrapped_line_prefix,
|
||||
const std::string wrapped_line_suffix) {
|
||||
std::istringstream in_stream(in);
|
||||
std::string wrapped, line, word;
|
||||
|
||||
in_stream >> word;
|
||||
line = word;
|
||||
|
||||
while (in_stream >> word) {
|
||||
if ((line.length() + 1 + word.length() + wrapped_line_suffix.length()) <
|
||||
max_length) {
|
||||
line += " " + word;
|
||||
} else {
|
||||
wrapped += line + wrapped_line_suffix + "\n";
|
||||
line = wrapped_line_prefix + word;
|
||||
}
|
||||
}
|
||||
wrapped += line;
|
||||
|
||||
return wrapped;
|
||||
}
|
||||
#endif // !FLATBUFFERS_PREFER_PRINTF
|
||||
|
||||
inline bool EscapeString(const char *s, size_t length, std::string *_text,
|
||||
bool allow_non_utf8, bool natural_utf8) {
|
||||
std::string &text = *_text;
|
||||
text += "\"";
|
||||
for (uoffset_t i = 0; i < length; i++) {
|
||||
char c = s[i];
|
||||
switch (c) {
|
||||
case '\n': text += "\\n"; break;
|
||||
case '\t': text += "\\t"; break;
|
||||
case '\r': text += "\\r"; break;
|
||||
case '\b': text += "\\b"; break;
|
||||
case '\f': text += "\\f"; break;
|
||||
case '\"': text += "\\\""; break;
|
||||
case '\\': text += "\\\\"; break;
|
||||
default:
|
||||
if (c >= ' ' && c <= '~') {
|
||||
text += c;
|
||||
} else {
|
||||
// Not printable ASCII data. Let's see if it's valid UTF-8 first:
|
||||
const char *utf8 = s + i;
|
||||
int ucc = FromUTF8(&utf8);
|
||||
if (ucc < 0) {
|
||||
if (allow_non_utf8) {
|
||||
text += "\\x";
|
||||
text += IntToStringHex(static_cast<uint8_t>(c), 2);
|
||||
} else {
|
||||
// There are two cases here:
|
||||
//
|
||||
// 1) We reached here by parsing an IDL file. In that case,
|
||||
// we previously checked for non-UTF-8, so we shouldn't reach
|
||||
// here.
|
||||
//
|
||||
// 2) We reached here by someone calling GenerateText()
|
||||
// on a previously-serialized flatbuffer. The data might have
|
||||
// non-UTF-8 Strings, or might be corrupt.
|
||||
//
|
||||
// In both cases, we have to give up and inform the caller
|
||||
// they have no JSON.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (natural_utf8) {
|
||||
// utf8 points to past all utf-8 bytes parsed
|
||||
text.append(s + i, static_cast<size_t>(utf8 - s - i));
|
||||
} else if (ucc <= 0xFFFF) {
|
||||
// Parses as Unicode within JSON's \uXXXX range, so use that.
|
||||
text += "\\u";
|
||||
text += IntToStringHex(ucc, 4);
|
||||
} else if (ucc <= 0x10FFFF) {
|
||||
// Encode Unicode SMP values to a surrogate pair using two \u
|
||||
// escapes.
|
||||
uint32_t base = ucc - 0x10000;
|
||||
auto high_surrogate = (base >> 10) + 0xD800;
|
||||
auto low_surrogate = (base & 0x03FF) + 0xDC00;
|
||||
text += "\\u";
|
||||
text += IntToStringHex(high_surrogate, 4);
|
||||
text += "\\u";
|
||||
text += IntToStringHex(low_surrogate, 4);
|
||||
}
|
||||
// Skip past characters recognized.
|
||||
i = static_cast<uoffset_t>(utf8 - s - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
text += "\"";
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::string BufferToHexText(const void *buffer, size_t buffer_size,
|
||||
size_t max_length,
|
||||
const std::string &wrapped_line_prefix,
|
||||
const std::string &wrapped_line_suffix) {
|
||||
std::string text = wrapped_line_prefix;
|
||||
size_t start_offset = 0;
|
||||
const char *s = reinterpret_cast<const char *>(buffer);
|
||||
for (size_t i = 0; s && i < buffer_size; i++) {
|
||||
// Last iteration or do we have more?
|
||||
bool have_more = i + 1 < buffer_size;
|
||||
text += "0x";
|
||||
text += IntToStringHex(static_cast<uint8_t>(s[i]), 2);
|
||||
if (have_more) { text += ','; }
|
||||
// If we have more to process and we reached max_length
|
||||
if (have_more &&
|
||||
text.size() + wrapped_line_suffix.size() >= start_offset + max_length) {
|
||||
text += wrapped_line_suffix;
|
||||
text += '\n';
|
||||
start_offset = text.size();
|
||||
text += wrapped_line_prefix;
|
||||
}
|
||||
}
|
||||
text += wrapped_line_suffix;
|
||||
return text;
|
||||
}
|
||||
|
||||
// Remove paired quotes in a string: "text"|'text' -> text.
|
||||
std::string RemoveStringQuotes(const std::string &s);
|
||||
|
||||
// Change th global C-locale to locale with name <locale_name>.
|
||||
// Returns an actual locale name in <_value>, useful if locale_name is "" or
|
||||
// null.
|
||||
bool SetGlobalTestLocale(const char *locale_name,
|
||||
std::string *_value = nullptr);
|
||||
|
||||
// Read (or test) a value of environment variable.
|
||||
bool ReadEnvironmentVariable(const char *var_name,
|
||||
std::string *_value = nullptr);
|
||||
|
||||
// MSVC specific: Send all assert reports to STDOUT to prevent CI hangs.
|
||||
void SetupDefaultCRTReportMode();
|
||||
|
||||
enum class Case {
|
||||
kUnknown = 0,
|
||||
// TheQuickBrownFox
|
||||
kUpperCamel = 1,
|
||||
// theQuickBrownFox
|
||||
kLowerCamel = 2,
|
||||
// the_quick_brown_fox
|
||||
kSnake = 3,
|
||||
// THE_QUICK_BROWN_FOX
|
||||
kScreamingSnake = 4,
|
||||
// THEQUICKBROWNFOX
|
||||
kAllUpper = 5,
|
||||
// thequickbrownfox
|
||||
kAllLower = 6,
|
||||
// the-quick-brown-fox
|
||||
kDasher = 7,
|
||||
// THEQuiCKBr_ownFox (or whatever you want, we won't change it)
|
||||
kKeep = 8,
|
||||
// the_quick_brown_fox123 (as opposed to the_quick_brown_fox_123)
|
||||
kSnake2 = 9,
|
||||
};
|
||||
|
||||
// Convert the `input` string of case `input_case` to the specified `output_case`.
|
||||
std::string ConvertCase(const std::string &input, Case output_case,
|
||||
Case input_case = Case::kSnake);
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_UTIL_H_
|
||||
@@ -1,389 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_VECTOR_H_
|
||||
#define FLATBUFFERS_VECTOR_H_
|
||||
|
||||
#include "flatbuffers/base.h"
|
||||
#include "flatbuffers/buffer.h"
|
||||
#include "flatbuffers/stl_emulation.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
struct String;
|
||||
|
||||
// An STL compatible iterator implementation for Vector below, effectively
|
||||
// calling Get() for every element.
|
||||
template<typename T, typename IT> struct VectorIterator {
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef IT value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef IT *pointer;
|
||||
typedef IT &reference;
|
||||
|
||||
VectorIterator(const uint8_t *data, uoffset_t i)
|
||||
: data_(data + IndirectHelper<T>::element_stride * i) {}
|
||||
VectorIterator(const VectorIterator &other) : data_(other.data_) {}
|
||||
VectorIterator() : data_(nullptr) {}
|
||||
|
||||
VectorIterator &operator=(const VectorIterator &other) {
|
||||
data_ = other.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VectorIterator &operator=(VectorIterator &&other) {
|
||||
data_ = other.data_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const VectorIterator &other) const {
|
||||
return data_ == other.data_;
|
||||
}
|
||||
|
||||
bool operator<(const VectorIterator &other) const {
|
||||
return data_ < other.data_;
|
||||
}
|
||||
|
||||
bool operator!=(const VectorIterator &other) const {
|
||||
return data_ != other.data_;
|
||||
}
|
||||
|
||||
difference_type operator-(const VectorIterator &other) const {
|
||||
return (data_ - other.data_) / IndirectHelper<T>::element_stride;
|
||||
}
|
||||
|
||||
// Note: return type is incompatible with the standard
|
||||
// `reference operator*()`.
|
||||
IT operator*() const { return IndirectHelper<T>::Read(data_, 0); }
|
||||
|
||||
// Note: return type is incompatible with the standard
|
||||
// `pointer operator->()`.
|
||||
IT operator->() const { return IndirectHelper<T>::Read(data_, 0); }
|
||||
|
||||
VectorIterator &operator++() {
|
||||
data_ += IndirectHelper<T>::element_stride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VectorIterator operator++(int) {
|
||||
VectorIterator temp(data_, 0);
|
||||
data_ += IndirectHelper<T>::element_stride;
|
||||
return temp;
|
||||
}
|
||||
|
||||
VectorIterator operator+(const uoffset_t &offset) const {
|
||||
return VectorIterator(data_ + offset * IndirectHelper<T>::element_stride,
|
||||
0);
|
||||
}
|
||||
|
||||
VectorIterator &operator+=(const uoffset_t &offset) {
|
||||
data_ += offset * IndirectHelper<T>::element_stride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VectorIterator &operator--() {
|
||||
data_ -= IndirectHelper<T>::element_stride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VectorIterator operator--(int) {
|
||||
VectorIterator temp(data_, 0);
|
||||
data_ -= IndirectHelper<T>::element_stride;
|
||||
return temp;
|
||||
}
|
||||
|
||||
VectorIterator operator-(const uoffset_t &offset) const {
|
||||
return VectorIterator(data_ - offset * IndirectHelper<T>::element_stride,
|
||||
0);
|
||||
}
|
||||
|
||||
VectorIterator &operator-=(const uoffset_t &offset) {
|
||||
data_ -= offset * IndirectHelper<T>::element_stride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t *data_;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct VectorReverseIterator : public std::reverse_iterator<Iterator> {
|
||||
explicit VectorReverseIterator(Iterator iter)
|
||||
: std::reverse_iterator<Iterator>(iter) {}
|
||||
|
||||
// Note: return type is incompatible with the standard
|
||||
// `reference operator*()`.
|
||||
typename Iterator::value_type operator*() const {
|
||||
auto tmp = std::reverse_iterator<Iterator>::current;
|
||||
return *--tmp;
|
||||
}
|
||||
|
||||
// Note: return type is incompatible with the standard
|
||||
// `pointer operator->()`.
|
||||
typename Iterator::value_type operator->() const {
|
||||
auto tmp = std::reverse_iterator<Iterator>::current;
|
||||
return *--tmp;
|
||||
}
|
||||
};
|
||||
|
||||
// This is used as a helper type for accessing vectors.
|
||||
// Vector::data() assumes the vector elements start after the length field.
|
||||
template<typename T> class Vector {
|
||||
public:
|
||||
typedef VectorIterator<T, typename IndirectHelper<T>::mutable_return_type>
|
||||
iterator;
|
||||
typedef VectorIterator<T, typename IndirectHelper<T>::return_type>
|
||||
const_iterator;
|
||||
typedef VectorReverseIterator<iterator> reverse_iterator;
|
||||
typedef VectorReverseIterator<const_iterator> const_reverse_iterator;
|
||||
|
||||
typedef typename flatbuffers::bool_constant<flatbuffers::is_scalar<T>::value>
|
||||
scalar_tag;
|
||||
|
||||
static FLATBUFFERS_CONSTEXPR bool is_span_observable =
|
||||
scalar_tag::value && (FLATBUFFERS_LITTLEENDIAN || sizeof(T) == 1);
|
||||
|
||||
uoffset_t size() const { return EndianScalar(length_); }
|
||||
|
||||
// Deprecated: use size(). Here for backwards compatibility.
|
||||
FLATBUFFERS_ATTRIBUTE([[deprecated("use size() instead")]])
|
||||
uoffset_t Length() const { return size(); }
|
||||
|
||||
typedef typename IndirectHelper<T>::return_type return_type;
|
||||
typedef typename IndirectHelper<T>::mutable_return_type mutable_return_type;
|
||||
typedef return_type value_type;
|
||||
|
||||
return_type Get(uoffset_t i) const {
|
||||
FLATBUFFERS_ASSERT(i < size());
|
||||
return IndirectHelper<T>::Read(Data(), i);
|
||||
}
|
||||
|
||||
return_type operator[](uoffset_t i) const { return Get(i); }
|
||||
|
||||
// If this is a Vector of enums, T will be its storage type, not the enum
|
||||
// type. This function makes it convenient to retrieve value with enum
|
||||
// type E.
|
||||
template<typename E> E GetEnum(uoffset_t i) const {
|
||||
return static_cast<E>(Get(i));
|
||||
}
|
||||
|
||||
// If this a vector of unions, this does the cast for you. There's no check
|
||||
// to make sure this is the right type!
|
||||
template<typename U> const U *GetAs(uoffset_t i) const {
|
||||
return reinterpret_cast<const U *>(Get(i));
|
||||
}
|
||||
|
||||
// If this a vector of unions, this does the cast for you. There's no check
|
||||
// to make sure this is actually a string!
|
||||
const String *GetAsString(uoffset_t i) const {
|
||||
return reinterpret_cast<const String *>(Get(i));
|
||||
}
|
||||
|
||||
const void *GetStructFromOffset(size_t o) const {
|
||||
return reinterpret_cast<const void *>(Data() + o);
|
||||
}
|
||||
|
||||
iterator begin() { return iterator(Data(), 0); }
|
||||
const_iterator begin() const { return const_iterator(Data(), 0); }
|
||||
|
||||
iterator end() { return iterator(Data(), size()); }
|
||||
const_iterator end() const { return const_iterator(Data(), size()); }
|
||||
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
const_reverse_iterator rbegin() const {
|
||||
return const_reverse_iterator(end());
|
||||
}
|
||||
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rend() const {
|
||||
return const_reverse_iterator(begin());
|
||||
}
|
||||
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
|
||||
const_iterator cend() const { return end(); }
|
||||
|
||||
const_reverse_iterator crbegin() const { return rbegin(); }
|
||||
|
||||
const_reverse_iterator crend() const { return rend(); }
|
||||
|
||||
// Change elements if you have a non-const pointer to this object.
|
||||
// Scalars only. See reflection.h, and the documentation.
|
||||
void Mutate(uoffset_t i, const T &val) {
|
||||
FLATBUFFERS_ASSERT(i < size());
|
||||
WriteScalar(data() + i, val);
|
||||
}
|
||||
|
||||
// Change an element of a vector of tables (or strings).
|
||||
// "val" points to the new table/string, as you can obtain from
|
||||
// e.g. reflection::AddFlatBuffer().
|
||||
void MutateOffset(uoffset_t i, const uint8_t *val) {
|
||||
FLATBUFFERS_ASSERT(i < size());
|
||||
static_assert(sizeof(T) == sizeof(uoffset_t), "Unrelated types");
|
||||
WriteScalar(data() + i,
|
||||
static_cast<uoffset_t>(val - (Data() + i * sizeof(uoffset_t))));
|
||||
}
|
||||
|
||||
// Get a mutable pointer to tables/strings inside this vector.
|
||||
mutable_return_type GetMutableObject(uoffset_t i) const {
|
||||
FLATBUFFERS_ASSERT(i < size());
|
||||
return const_cast<mutable_return_type>(IndirectHelper<T>::Read(Data(), i));
|
||||
}
|
||||
|
||||
// The raw data in little endian format. Use with care.
|
||||
const uint8_t *Data() const {
|
||||
return reinterpret_cast<const uint8_t *>(&length_ + 1);
|
||||
}
|
||||
|
||||
uint8_t *Data() { return reinterpret_cast<uint8_t *>(&length_ + 1); }
|
||||
|
||||
// Similarly, but typed, much like std::vector::data
|
||||
const T *data() const { return reinterpret_cast<const T *>(Data()); }
|
||||
T *data() { return reinterpret_cast<T *>(Data()); }
|
||||
|
||||
template<typename K> return_type LookupByKey(K key) const {
|
||||
void *search_result = std::bsearch(
|
||||
&key, Data(), size(), IndirectHelper<T>::element_stride, KeyCompare<K>);
|
||||
|
||||
if (!search_result) {
|
||||
return nullptr; // Key not found.
|
||||
}
|
||||
|
||||
const uint8_t *element = reinterpret_cast<const uint8_t *>(search_result);
|
||||
|
||||
return IndirectHelper<T>::Read(element, 0);
|
||||
}
|
||||
|
||||
template<typename K> mutable_return_type MutableLookupByKey(K key) {
|
||||
return const_cast<mutable_return_type>(LookupByKey(key));
|
||||
}
|
||||
|
||||
protected:
|
||||
// This class is only used to access pre-existing data. Don't ever
|
||||
// try to construct these manually.
|
||||
Vector();
|
||||
|
||||
uoffset_t length_;
|
||||
|
||||
private:
|
||||
// This class is a pointer. Copying will therefore create an invalid object.
|
||||
// Private and unimplemented copy constructor.
|
||||
Vector(const Vector &);
|
||||
Vector &operator=(const Vector &);
|
||||
|
||||
template<typename K> static int KeyCompare(const void *ap, const void *bp) {
|
||||
const K *key = reinterpret_cast<const K *>(ap);
|
||||
const uint8_t *data = reinterpret_cast<const uint8_t *>(bp);
|
||||
auto table = IndirectHelper<T>::Read(data, 0);
|
||||
|
||||
// std::bsearch compares with the operands transposed, so we negate the
|
||||
// result here.
|
||||
return -table->KeyCompareWithValue(*key);
|
||||
}
|
||||
};
|
||||
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<U> make_span(Vector<U> &vec)
|
||||
FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::is_span_observable,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return span<U>(vec.data(), vec.size());
|
||||
}
|
||||
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<const U> make_span(
|
||||
const Vector<U> &vec) FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::is_span_observable,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return span<const U>(vec.data(), vec.size());
|
||||
}
|
||||
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<uint8_t> make_bytes_span(
|
||||
Vector<U> &vec) FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::scalar_tag::value,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return span<uint8_t>(vec.Data(), vec.size() * sizeof(U));
|
||||
}
|
||||
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<const uint8_t> make_bytes_span(
|
||||
const Vector<U> &vec) FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::scalar_tag::value,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return span<const uint8_t>(vec.Data(), vec.size() * sizeof(U));
|
||||
}
|
||||
|
||||
// Convenient helper functions to get a span of any vector, regardless
|
||||
// of whether it is null or not (the field is not set).
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<U> make_span(Vector<U> *ptr)
|
||||
FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::is_span_observable,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return ptr ? make_span(*ptr) : span<U>();
|
||||
}
|
||||
|
||||
template<class U>
|
||||
FLATBUFFERS_CONSTEXPR_CPP11 flatbuffers::span<const U> make_span(
|
||||
const Vector<U> *ptr) FLATBUFFERS_NOEXCEPT {
|
||||
static_assert(Vector<U>::is_span_observable,
|
||||
"wrong type U, only LE-scalar, or byte types are allowed");
|
||||
return ptr ? make_span(*ptr) : span<const U>();
|
||||
}
|
||||
|
||||
// Represent a vector much like the template above, but in this case we
|
||||
// don't know what the element types are (used with reflection.h).
|
||||
class VectorOfAny {
|
||||
public:
|
||||
uoffset_t size() const { return EndianScalar(length_); }
|
||||
|
||||
const uint8_t *Data() const {
|
||||
return reinterpret_cast<const uint8_t *>(&length_ + 1);
|
||||
}
|
||||
uint8_t *Data() { return reinterpret_cast<uint8_t *>(&length_ + 1); }
|
||||
|
||||
protected:
|
||||
VectorOfAny();
|
||||
|
||||
uoffset_t length_;
|
||||
|
||||
private:
|
||||
VectorOfAny(const VectorOfAny &);
|
||||
VectorOfAny &operator=(const VectorOfAny &);
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
Vector<Offset<T>> *VectorCast(Vector<Offset<U>> *ptr) {
|
||||
static_assert(std::is_base_of<T, U>::value, "Unrelated types");
|
||||
return reinterpret_cast<Vector<Offset<T>> *>(ptr);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
const Vector<Offset<T>> *VectorCast(const Vector<Offset<U>> *ptr) {
|
||||
static_assert(std::is_base_of<T, U>::value, "Unrelated types");
|
||||
return reinterpret_cast<const Vector<Offset<T>> *>(ptr);
|
||||
}
|
||||
|
||||
// Convenient helper function to get the length of any vector, regardless
|
||||
// of whether it is null or not (the field is not set).
|
||||
template<typename T> static inline size_t VectorLength(const Vector<T> *v) {
|
||||
return v ? v->size() : 0;
|
||||
}
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_VERIFIER_H_
|
||||
@@ -1,304 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 Google Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef FLATBUFFERS_VERIFIER_H_
|
||||
#define FLATBUFFERS_VERIFIER_H_
|
||||
|
||||
#include "flatbuffers/base.h"
|
||||
#include "flatbuffers/vector.h"
|
||||
|
||||
namespace flatbuffers {
|
||||
|
||||
// Helper class to verify the integrity of a FlatBuffer
|
||||
class Verifier FLATBUFFERS_FINAL_CLASS {
|
||||
public:
|
||||
Verifier(const uint8_t *const buf, const size_t buf_len,
|
||||
const uoffset_t _max_depth = 64,
|
||||
const uoffset_t _max_tables = 1000000,
|
||||
const bool _check_alignment = true)
|
||||
: buf_(buf),
|
||||
size_(buf_len),
|
||||
max_depth_(_max_depth),
|
||||
max_tables_(_max_tables),
|
||||
check_alignment_(_check_alignment),
|
||||
upper_bound_(0),
|
||||
depth_(0),
|
||||
num_tables_(0),
|
||||
flex_reuse_tracker_(nullptr) {
|
||||
FLATBUFFERS_ASSERT(size_ < FLATBUFFERS_MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
// Central location where any verification failures register.
|
||||
bool Check(const bool ok) const {
|
||||
// clang-format off
|
||||
#ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE
|
||||
FLATBUFFERS_ASSERT(ok);
|
||||
#endif
|
||||
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
|
||||
if (!ok)
|
||||
upper_bound_ = 0;
|
||||
#endif
|
||||
// clang-format on
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Verify any range within the buffer.
|
||||
bool Verify(const size_t elem, const size_t elem_len) const {
|
||||
// clang-format off
|
||||
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
|
||||
auto upper_bound = elem + elem_len;
|
||||
if (upper_bound_ < upper_bound)
|
||||
upper_bound_ = upper_bound;
|
||||
#endif
|
||||
// clang-format on
|
||||
return Check(elem_len < size_ && elem <= size_ - elem_len);
|
||||
}
|
||||
|
||||
bool VerifyAlignment(const size_t elem, const size_t align) const {
|
||||
return Check((elem & (align - 1)) == 0 || !check_alignment_);
|
||||
}
|
||||
|
||||
// Verify a range indicated by sizeof(T).
|
||||
template<typename T> bool Verify(const size_t elem) const {
|
||||
return VerifyAlignment(elem, sizeof(T)) && Verify(elem, sizeof(T));
|
||||
}
|
||||
|
||||
bool VerifyFromPointer(const uint8_t *const p, const size_t len) {
|
||||
return Verify(static_cast<size_t>(p - buf_), len);
|
||||
}
|
||||
|
||||
// Verify relative to a known-good base pointer.
|
||||
bool VerifyFieldStruct(const uint8_t *const base, const voffset_t elem_off,
|
||||
const size_t elem_len, const size_t align) const {
|
||||
const auto f = static_cast<size_t>(base - buf_) + elem_off;
|
||||
return VerifyAlignment(f, align) && Verify(f, elem_len);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool VerifyField(const uint8_t *const base, const voffset_t elem_off,
|
||||
const size_t align) const {
|
||||
const auto f = static_cast<size_t>(base - buf_) + elem_off;
|
||||
return VerifyAlignment(f, align) && Verify(f, sizeof(T));
|
||||
}
|
||||
|
||||
// Verify a pointer (may be NULL) of a table type.
|
||||
template<typename T> bool VerifyTable(const T *const table) {
|
||||
return !table || table->Verify(*this);
|
||||
}
|
||||
|
||||
// Verify a pointer (may be NULL) of any vector type.
|
||||
template<typename T> bool VerifyVector(const Vector<T> *const vec) const {
|
||||
return !vec || VerifyVectorOrString(reinterpret_cast<const uint8_t *>(vec),
|
||||
sizeof(T));
|
||||
}
|
||||
|
||||
// Verify a pointer (may be NULL) of a vector to struct.
|
||||
template<typename T>
|
||||
bool VerifyVector(const Vector<const T *> *const vec) const {
|
||||
return VerifyVector(reinterpret_cast<const Vector<T> *>(vec));
|
||||
}
|
||||
|
||||
// Verify a pointer (may be NULL) to string.
|
||||
bool VerifyString(const String *const str) const {
|
||||
size_t end;
|
||||
return !str || (VerifyVectorOrString(reinterpret_cast<const uint8_t *>(str),
|
||||
1, &end) &&
|
||||
Verify(end, 1) && // Must have terminator
|
||||
Check(buf_[end] == '\0')); // Terminating byte must be 0.
|
||||
}
|
||||
|
||||
// Common code between vectors and strings.
|
||||
bool VerifyVectorOrString(const uint8_t *const vec, const size_t elem_size,
|
||||
size_t *const end = nullptr) const {
|
||||
const auto veco = static_cast<size_t>(vec - buf_);
|
||||
// Check we can read the size field.
|
||||
if (!Verify<uoffset_t>(veco)) return false;
|
||||
// Check the whole array. If this is a string, the byte past the array
|
||||
// must be 0.
|
||||
const auto size = ReadScalar<uoffset_t>(vec);
|
||||
const auto max_elems = FLATBUFFERS_MAX_BUFFER_SIZE / elem_size;
|
||||
if (!Check(size < max_elems))
|
||||
return false; // Protect against byte_size overflowing.
|
||||
const auto byte_size = sizeof(size) + elem_size * size;
|
||||
if (end) *end = veco + byte_size;
|
||||
return Verify(veco, byte_size);
|
||||
}
|
||||
|
||||
// Special case for string contents, after the above has been called.
|
||||
bool VerifyVectorOfStrings(const Vector<Offset<String>> *const vec) const {
|
||||
if (vec) {
|
||||
for (uoffset_t i = 0; i < vec->size(); i++) {
|
||||
if (!VerifyString(vec->Get(i))) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Special case for table contents, after the above has been called.
|
||||
template<typename T>
|
||||
bool VerifyVectorOfTables(const Vector<Offset<T>> *const vec) {
|
||||
if (vec) {
|
||||
for (uoffset_t i = 0; i < vec->size(); i++) {
|
||||
if (!vec->Get(i)->Verify(*this)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
__supress_ubsan__("unsigned-integer-overflow") bool VerifyTableStart(
|
||||
const uint8_t *const table) {
|
||||
// Check the vtable offset.
|
||||
const auto tableo = static_cast<size_t>(table - buf_);
|
||||
if (!Verify<soffset_t>(tableo)) return false;
|
||||
// This offset may be signed, but doing the subtraction unsigned always
|
||||
// gives the result we want.
|
||||
const auto vtableo =
|
||||
tableo - static_cast<size_t>(ReadScalar<soffset_t>(table));
|
||||
// Check the vtable size field, then check vtable fits in its entirety.
|
||||
if (!(VerifyComplexity() && Verify<voffset_t>(vtableo) &&
|
||||
VerifyAlignment(ReadScalar<voffset_t>(buf_ + vtableo),
|
||||
sizeof(voffset_t))))
|
||||
return false;
|
||||
const auto vsize = ReadScalar<voffset_t>(buf_ + vtableo);
|
||||
return Check((vsize & 1) == 0) && Verify(vtableo, vsize);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool VerifyBufferFromStart(const char *const identifier, const size_t start) {
|
||||
// Buffers have to be of some size to be valid. The reason it is a runtime
|
||||
// check instead of static_assert, is that nested flatbuffers go through
|
||||
// this call and their size is determined at runtime.
|
||||
if (!Check(size_ >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
|
||||
|
||||
// If an identifier is provided, check that we have a buffer
|
||||
if (identifier && !Check((size_ >= 2 * sizeof(flatbuffers::uoffset_t) &&
|
||||
BufferHasIdentifier(buf_ + start, identifier)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call T::Verify, which must be in the generated code for this type.
|
||||
const auto o = VerifyOffset(start);
|
||||
return Check(o != 0) &&
|
||||
reinterpret_cast<const T *>(buf_ + start + o)->Verify(*this)
|
||||
// clang-format off
|
||||
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
|
||||
&& GetComputedSize()
|
||||
#endif
|
||||
;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool VerifyNestedFlatBuffer(const Vector<uint8_t> *const buf,
|
||||
const char *const identifier) {
|
||||
// An empty buffer is OK as it indicates not present.
|
||||
if (!buf) return true;
|
||||
|
||||
// If there is a nested buffer, it must be greater than the min size.
|
||||
if(!Check(buf->size() >= FLATBUFFERS_MIN_BUFFER_SIZE)) return false;
|
||||
|
||||
Verifier nested_verifier(buf->data(), buf->size());
|
||||
return nested_verifier.VerifyBuffer<T>(identifier);
|
||||
}
|
||||
|
||||
// Verify this whole buffer, starting with root type T.
|
||||
template<typename T> bool VerifyBuffer() { return VerifyBuffer<T>(nullptr); }
|
||||
|
||||
template<typename T> bool VerifyBuffer(const char *const identifier) {
|
||||
return VerifyBufferFromStart<T>(identifier, 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool VerifySizePrefixedBuffer(const char *const identifier) {
|
||||
return Verify<uoffset_t>(0U) &&
|
||||
Check(ReadScalar<uoffset_t>(buf_) == size_ - sizeof(uoffset_t)) &&
|
||||
VerifyBufferFromStart<T>(identifier, sizeof(uoffset_t));
|
||||
}
|
||||
|
||||
uoffset_t VerifyOffset(const size_t start) const {
|
||||
if (!Verify<uoffset_t>(start)) return 0;
|
||||
const auto o = ReadScalar<uoffset_t>(buf_ + start);
|
||||
// May not point to itself.
|
||||
if (!Check(o != 0)) return 0;
|
||||
// Can't wrap around / buffers are max 2GB.
|
||||
if (!Check(static_cast<soffset_t>(o) >= 0)) return 0;
|
||||
// Must be inside the buffer to create a pointer from it (pointer outside
|
||||
// buffer is UB).
|
||||
if (!Verify(start + o, 1)) return 0;
|
||||
return o;
|
||||
}
|
||||
|
||||
uoffset_t VerifyOffset(const uint8_t *const base,
|
||||
const voffset_t start) const {
|
||||
return VerifyOffset(static_cast<size_t>(base - buf_) + start);
|
||||
}
|
||||
|
||||
// Called at the start of a table to increase counters measuring data
|
||||
// structure depth and amount, and possibly bails out with false if
|
||||
// limits set by the constructor have been hit. Needs to be balanced
|
||||
// with EndTable().
|
||||
bool VerifyComplexity() {
|
||||
depth_++;
|
||||
num_tables_++;
|
||||
return Check(depth_ <= max_depth_ && num_tables_ <= max_tables_);
|
||||
}
|
||||
|
||||
// Called at the end of a table to pop the depth count.
|
||||
bool EndTable() {
|
||||
depth_--;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the message size in bytes
|
||||
size_t GetComputedSize() const {
|
||||
// clang-format off
|
||||
#ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE
|
||||
uintptr_t size = upper_bound_;
|
||||
// Align the size to uoffset_t
|
||||
size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1);
|
||||
return (size > size_) ? 0 : size;
|
||||
#else
|
||||
// Must turn on FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE for this to work.
|
||||
(void)upper_bound_;
|
||||
FLATBUFFERS_ASSERT(false);
|
||||
return 0;
|
||||
#endif
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
std::vector<uint8_t> *GetFlexReuseTracker() { return flex_reuse_tracker_; }
|
||||
|
||||
void SetFlexReuseTracker(std::vector<uint8_t> *const rt) {
|
||||
flex_reuse_tracker_ = rt;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t *buf_;
|
||||
const size_t size_;
|
||||
const uoffset_t max_depth_;
|
||||
const uoffset_t max_tables_;
|
||||
const bool check_alignment_;
|
||||
|
||||
mutable size_t upper_bound_;
|
||||
|
||||
uoffset_t depth_;
|
||||
uoffset_t num_tables_;
|
||||
std::vector<uint8_t> *flex_reuse_tracker_;
|
||||
};
|
||||
|
||||
} // namespace flatbuffers
|
||||
|
||||
#endif // FLATBUFFERS_VERIFIER_H_
|
||||
Reference in New Issue
Block a user