Skip to content

Commit 6fc405d

Browse files
authored
Automated sync from github.com/tensorflow/tensorflow (#3033)
BUG=automated sync from upstream NO_CHECK_TFLITE_FILES=automated sync from upstream
1 parent db51e8d commit 6fc405d

File tree

8 files changed

+299
-5
lines changed

8 files changed

+299
-5
lines changed

tensorflow/lite/core/c/c_api_types.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ extern "C" {
6161
#ifdef TFL_COMPILE_LIBRARY
6262
#define TFL_CAPI_EXPORT __declspec(dllexport)
6363
#else
64-
#define TFL_CAPI_EXPORT __declspec(dllimport)
64+
#define TFL_CAPI_EXPORT
6565
#endif // TFL_COMPILE_LIBRARY
6666
#else
6767
#define TFL_CAPI_EXPORT __attribute__((visibility("default")))

tensorflow/lite/core/c/common.cc

+123-2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,60 @@ void TfLiteVarArrayFree(T* a) {
102102
free(a);
103103
}
104104

105+
#ifndef TF_LITE_STATIC_MEMORY
106+
107+
TfLiteQuantization TfLiteQuantizationClone(const TfLiteQuantization& src) {
108+
TfLiteQuantization dst;
109+
dst.type = src.type;
110+
switch (src.type) {
111+
case kTfLiteNoQuantization:
112+
break;
113+
case kTfLiteAffineQuantization: {
114+
dst.params = calloc(1, sizeof(TfLiteAffineQuantization));
115+
const TfLiteAffineQuantization* const src_params =
116+
(TfLiteAffineQuantization*)(src.params);
117+
TfLiteAffineQuantization* const dst_params =
118+
(TfLiteAffineQuantization*)(dst.params);
119+
dst_params->quantized_dimension = src_params->quantized_dimension;
120+
dst_params->scale = TfLiteFloatArrayCopy(src_params->scale);
121+
dst_params->zero_point = TfLiteIntArrayCopy(src_params->zero_point);
122+
break;
123+
}
124+
}
125+
return dst;
126+
}
127+
128+
TfLiteSparsity TfLiteSparsityClone(const TfLiteSparsity& src) {
129+
TfLiteSparsity dst = src;
130+
dst.traversal_order = TfLiteIntArrayCopy(src.traversal_order);
131+
dst.block_map = TfLiteIntArrayCopy(src.block_map);
132+
if (src.dim_metadata) {
133+
dst.dim_metadata = reinterpret_cast<TfLiteDimensionMetadata*>(
134+
calloc(1, sizeof(TfLiteDimensionMetadata) * src.dim_metadata_size));
135+
for (int i = 0; i < src.dim_metadata_size; ++i) {
136+
dst.dim_metadata[i] = src.dim_metadata[i];
137+
dst.dim_metadata[i].array_segments =
138+
TfLiteIntArrayCopy(src.dim_metadata[i].array_segments);
139+
dst.dim_metadata[i].array_indices =
140+
TfLiteIntArrayCopy(src.dim_metadata[i].array_indices);
141+
}
142+
}
143+
return dst;
144+
}
145+
146+
// Clones the source sparsity to a newly allocated object.
147+
TfLiteSparsity* TfLiteSparsityClone(const TfLiteSparsity* const src) {
148+
if (!src) {
149+
return nullptr;
150+
}
151+
TfLiteSparsity* dst =
152+
reinterpret_cast<TfLiteSparsity*>(calloc(1, sizeof(TfLiteSparsity)));
153+
*dst = TfLiteSparsityClone(*src);
154+
return dst;
155+
}
156+
157+
#endif // TF_LITE_STATIC_MEMORY
158+
105159
} // namespace
106160

107161
extern "C" {
@@ -234,6 +288,55 @@ void TfLiteTensorFree(TfLiteTensor* t) {
234288
t->sparsity = nullptr;
235289
}
236290

291+
TfLiteTensor TfLiteTensorClone(const TfLiteTensor src) {
292+
// We copy all of the source data first, then we clone the fields that can't
293+
// be shared between two tensor instances.
294+
TfLiteTensor dst = src;
295+
// Data that is owned by the original tensor mut be cloned. Check
296+
// TfLiteTensorFree to find out which members are owned.
297+
if (src.data.data) {
298+
const TfLiteAllocationStrategy allocation_strategy =
299+
TfLiteTensorGetAllocationStrategy(&src);
300+
switch (allocation_strategy) {
301+
case kTfLiteAllocationStrategyUnknown:
302+
// We don't know the allocation strategy, which means that the tensor
303+
// doesn't own its data: we keep the copied pointer to the data.
304+
break;
305+
case kTfLiteAllocationStrategyNone:
306+
break;
307+
case kTfLiteAllocationStrategyMMap:
308+
// Mmapped data is read-only and external to the interpreter. We keep
309+
// the copied pointer to the data.
310+
break;
311+
case kTfLiteAllocationStrategyArena:
312+
// Arena tensors are allocated when the graph is prepared. There is no
313+
// data associated to such a tensor between runs so we don't care about
314+
// the value of `data`.
315+
break;
316+
case kTfLiteAllocationStrategyMalloc:
317+
dst.data.data = malloc(src.bytes);
318+
std::memcpy(dst.data.data, src.data.data, src.bytes);
319+
break;
320+
case kTfLiteAllocationStrategyNew:
321+
// Special case for variant objects. They are allocated using new/delete
322+
// but require using the `CloneTo` function.
323+
if (src.allocation_type == kTfLiteVariantObject) {
324+
dst.data.data = reinterpret_cast<const VariantData*>(src.data.data)
325+
->CloneTo(nullptr);
326+
} else {
327+
dst.data.data = new char[src.bytes];
328+
std::memcpy(dst.data.data, src.data.data, src.bytes);
329+
}
330+
break;
331+
}
332+
}
333+
dst.dims = TfLiteIntArrayCopy(src.dims);
334+
dst.dims_signature = TfLiteIntArrayCopy(src.dims_signature);
335+
dst.quantization = TfLiteQuantizationClone(src.quantization);
336+
dst.sparsity = TfLiteSparsityClone(src.sparsity);
337+
return dst;
338+
}
339+
237340
void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
238341
TfLiteQuantizationParams quantization, char* buffer,
239342
size_t size, TfLiteAllocationType allocation_type,
@@ -334,6 +437,14 @@ TfLiteStatus TfLiteTensorResizeMaybeCopy(size_t num_bytes, TfLiteTensor* tensor,
334437
TfLiteStatus TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor) {
335438
return TfLiteTensorResizeMaybeCopy(num_bytes, tensor, true);
336439
}
440+
441+
const TfLiteIntArray* TfLiteTensorGetDimsSignature(const TfLiteTensor* t) {
442+
if (t->dims_signature != nullptr && t->dims_signature->size != 0) {
443+
return t->dims_signature;
444+
} else {
445+
return t->dims;
446+
}
447+
}
337448
#endif // TF_LITE_STATIC_MEMORY
338449

339450
const char* TfLiteTypeGetName(TfLiteType type) {
@@ -399,11 +510,13 @@ TfLiteAllocationStrategy TfLiteTensorGetAllocationStrategy(
399510
case kTfLiteDynamic:
400511
return kTfLiteAllocationStrategyMalloc;
401512
case kTfLitePersistentRo:
402-
return kTfLiteAllocationStrategyUnknown;
513+
return kTfLiteAllocationStrategyMalloc;
403514
case kTfLiteCustom:
404515
return kTfLiteAllocationStrategyUnknown;
405516
case kTfLiteVariantObject:
406517
return kTfLiteAllocationStrategyNew;
518+
case kTfLiteNonCpu:
519+
return kTfLiteAllocationStrategyUnknown;
407520
}
408521
return kTfLiteAllocationStrategyUnknown;
409522
}
@@ -428,6 +541,8 @@ TfLiteRunStability TfLiteTensorGetBufferAddressStability(
428541
return kTfLiteRunStabilityUnknown;
429542
case kTfLiteVariantObject:
430543
return kTfLiteRunStabilityAcrossRuns;
544+
case kTfLiteNonCpu:
545+
return kTfLiteRunStabilityUnknown;
431546
}
432547
return kTfLiteRunStabilityUnknown;
433548
}
@@ -451,6 +566,8 @@ TfLiteRunStability TfLiteTensorGetDataStability(const TfLiteTensor* const t) {
451566
return kTfLiteRunStabilityUnknown;
452567
case kTfLiteVariantObject:
453568
return kTfLiteRunStabilitySingleRun;
569+
case kTfLiteNonCpu:
570+
return kTfLiteRunStabilityUnknown;
454571
}
455572
return kTfLiteRunStabilityUnknown;
456573
}
@@ -477,11 +594,13 @@ TfLiteRunStep TfLiteTensorGetDataKnownStep(const TfLiteTensor* t) {
477594
return kTfLiteRunStepUnknown;
478595
case kTfLiteVariantObject:
479596
return kTfLiteRunStepEval;
597+
case kTfLiteNonCpu:
598+
return kTfLiteRunStepUnknown;
480599
}
481600
return kTfLiteRunStepUnknown;
482601
}
483602

484-
// Returns the operation steop when the shape of a tensor is computed.
603+
// Returns the operation step when the shape of a tensor is computed.
485604
//
486605
// Some operations can precompute the shape of their results before the
487606
// evaluation step. This makes the shape available earlier for subsequent
@@ -504,6 +623,8 @@ TfLiteRunStep TfLiteTensorGetShapeKnownStep(const TfLiteTensor* t) {
504623
return kTfLiteRunStepUnknown;
505624
case kTfLiteVariantObject:
506625
return kTfLiteRunStepEval;
626+
case kTfLiteNonCpu:
627+
return kTfLiteRunStepUnknown;
507628
}
508629
return kTfLiteRunStepUnknown;
509630
}

tensorflow/lite/core/c/common.h

+17-2
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ typedef union TfLitePtrUnion {
396396
/// * `kTfLiteVariantObject`: Allocation is an arbitrary type-erased C++
397397
/// object.
398398
/// Allocation and deallocation are done through `new` and `delete`.
399+
/// * `kTfLiteNonCpu`: Tensor buffer is in non-CPU memory, such as AHWB, GPU
400+
/// memory. This tensor is not accessed by the CPU.
401+
/// This is only used by LiteRt API.
399402
typedef enum TfLiteAllocationType {
400403
kTfLiteMemNone = 0,
401404
kTfLiteMmapRo,
@@ -405,6 +408,7 @@ typedef enum TfLiteAllocationType {
405408
kTfLitePersistentRo,
406409
kTfLiteCustom,
407410
kTfLiteVariantObject,
411+
kTfLiteNonCpu,
408412
} TfLiteAllocationType;
409413

410414
/// Memory allocation strategies.
@@ -553,8 +557,10 @@ typedef struct TfLiteTensor {
553557
/// only populated when unknown dimensions exist in a read-write tensor (i.e.
554558
/// an input or output tensor). (e.g. `dims` contains [1, 1, 1, 3] and
555559
/// `dims_signature` contains [1, -1, -1, 3]). If no unknown dimensions exist
556-
/// then `dims_signature` is either null, or set to an empty array. Note that
557-
/// this field only exists when TF_LITE_STATIC_MEMORY is not defined.
560+
/// then `dims_signature` is either null, or set to an empty array. Use
561+
/// `TfLiteTensorGetDimsSignature` to get `dims_signature` if non-empty or
562+
/// otherwise fallback to `dims`. Note that this field only exists when
563+
/// TF_LITE_STATIC_MEMORY is not defined.
558564
const TfLiteIntArray* dims_signature;
559565
} TfLiteTensor;
560566

@@ -734,6 +740,9 @@ void TfLiteTensorReset(TfLiteType type, const char* name, TfLiteIntArray* dims,
734740
/// quantization, sparsity, ...
735741
TfLiteStatus TfLiteTensorCopy(const TfLiteTensor* src, TfLiteTensor* dst);
736742

743+
/// Returns a tensor holding a deep copy of src.
744+
TfLiteTensor TfLiteTensorClone(TfLiteTensor src);
745+
737746
/// Change the size of the memory block owned by `tensor` to `num_bytes`.
738747
/// Tensors with allocation types other than `kTfLiteDynamic` will be ignored
739748
/// and a `kTfLiteOk` will be returned. `tensor`'s internal data buffer will be
@@ -753,6 +762,12 @@ TfLiteStatus TfLiteTensorResizeMaybeCopy(size_t num_bytes, TfLiteTensor* tensor,
753762
/// start of the region up to the minimum of the old and new sizes. In the case
754763
/// of NULL tensor, or an error allocating new memory, returns `kTfLiteError`.
755764
TfLiteStatus TfLiteTensorRealloc(size_t num_bytes, TfLiteTensor* tensor);
765+
766+
/// Returns the shape of the tensor, with -1 for any unknown dimension sizes.
767+
/// If any dimension is unknown, this is the same as `t->dims_signature`.
768+
/// If all dimensions are known, this is the same as `t->dims`.
769+
/// (`dims_signature` is NULL or empty if all dimensions are known.)
770+
const TfLiteIntArray* TfLiteTensorGetDimsSignature(const TfLiteTensor* t);
756771
#endif // TF_LITE_STATIC_MEMORY
757772

758773
/// WARNING: This is an experimental interface that is subject to change.

tensorflow/lite/kernels/internal/portable_tensor_utils.cc

+20
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,26 @@ void UnpackDenseInt4IntoInt8(const int8_t* src_buffer, int num_elements,
9292
}
9393
}
9494

95+
void PackInt8IntoDenseInt4(const int8_t* src_buffer, int num_elements,
96+
int8_t* dst_buffer) {
97+
// num_elements means the number of elements regardless of packed or unpacked.
98+
// For example, 3 elements means both
99+
// 1) Packed: 3 int4's = 12 bit -> 16 bits (padded) = 2 bytes.
100+
// stored in src_buffer[0] and src_buffer[1] (i = 0..1)
101+
// 2) Unpacked: 3 int8's = 3 bytes.
102+
// stored in dst_buffer[0], dst_buffer[1] and dst_buffer[2] (j = 0..2)
103+
for (int i = 0; i < num_elements - 1; i += 2) {
104+
dst_buffer[i / 2] = src_buffer[i] & 0x0F;
105+
dst_buffer[i / 2] |= src_buffer[i + 1] << 4;
106+
}
107+
auto packed_size = (num_elements + 1) / 2;
108+
109+
// Copy the final nibble if the buffer is odd-lengthed
110+
if (num_elements % 2 != 0) {
111+
dst_buffer[packed_size - 1] = src_buffer[num_elements - 1] & 0x0F;
112+
}
113+
}
114+
95115
} // namespace tensor_utils
96116
} // namespace tflite
97117

tensorflow/lite/kernels/internal/portable_tensor_utils.h

+14
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,20 @@ void ApplySignbitToVector(const float* __restrict__ vector, int v_size,
617617
void UnpackDenseInt4IntoInt8(const int8_t* src_buffer, int num_elements,
618618
int8_t* dst_buffer);
619619

620+
// Pack `src_buffer` into a densely packed buffer of int4 values.
621+
// Parameters:
622+
// src_buffer : Buffer containing int4 values stored in int8 memory.
623+
// num_elements : Number of elements stored in the buffer. Note that this can
624+
// be smaller than the size of `src_buffer` by 1 if it's odd,
625+
// in which case the last nibble in `src_buffer` is ignored.
626+
// This should be equal to the size of `dst_buffer`.
627+
// dst_buffer : Buffer to pack into. Should be allocated by the caller.
628+
// Size should be at least `num_elements`.
629+
// Notes:
630+
// For example, given `src_buffer = {0x02, 0x01, 0x04, 0x03}`, calling this
631+
// function will return `dst_buffer = {0x12, 0x34}`.
632+
void PackInt8IntoDenseInt4(const int8_t* src_buffer, int num_elements,
633+
int8_t* dst_buffer);
620634
} // namespace tensor_utils
621635

622636
} // namespace tflite

tensorflow/lite/tools/flatbuffer_utils.py

+68
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
"""
2222

2323
import copy
24+
import functools
2425
import random
2526
import re
2627
import struct
2728
import sys
29+
from typing import Optional, Type, TypeVar, Union
2830

2931
import flatbuffers
3032

@@ -453,3 +455,69 @@ def count_resource_variables(model):
453455
if builtin_code == schema_fb.BuiltinOperator.VAR_HANDLE:
454456
unique_shared_names.add(op.builtinOptions.sharedName)
455457
return len(unique_shared_names)
458+
459+
460+
OptsT = TypeVar('OptsT')
461+
462+
463+
def get_options_as(
464+
op: Union[schema_fb.Operator, schema_fb.OperatorT], opts_type: Type[OptsT]
465+
) -> Optional[OptsT]:
466+
"""Get the options of an operator as the specified type.
467+
468+
Requested type must be an object-api type (ends in 'T').
469+
470+
Args:
471+
op: The operator to get the options from.
472+
opts_type: The type of the options to get.
473+
474+
Returns:
475+
The options as the specified type, or None if the options are not of the
476+
specified type.
477+
478+
Raises:
479+
ValueError: If the specified type is not a valid options type.
480+
"""
481+
482+
err = ValueError(f'Unsupported options type: {opts_type}')
483+
type_name: str = opts_type.__name__
484+
if not type_name.endswith('T'):
485+
raise err
486+
base_type_name = type_name.removesuffix('T')
487+
is_opt_1_type = hasattr(schema_fb.BuiltinOptions, base_type_name)
488+
if not is_opt_1_type and not hasattr(
489+
schema_fb.BuiltinOptions2, base_type_name
490+
):
491+
raise err
492+
493+
@functools.singledispatch
494+
def _get_opts(unused_op):
495+
return None
496+
497+
@_get_opts.register
498+
def _(op: schema_fb.Operator):
499+
if not is_opt_1_type:
500+
enum_val = getattr(schema_fb.BuiltinOptions2, base_type_name)
501+
opts_creator = schema_fb.BuiltinOptions2Creator
502+
raw_ops = op.BuiltinOptions2()
503+
actual_enum_val = op.BuiltinOptions2Type()
504+
else:
505+
enum_val = getattr(schema_fb.BuiltinOptions, base_type_name)
506+
opts_creator = schema_fb.BuiltinOptionsCreator
507+
raw_ops = op.BuiltinOptions()
508+
actual_enum_val = op.BuiltinOptionsType()
509+
if raw_ops is None or actual_enum_val != enum_val:
510+
return None
511+
return opts_creator(enum_val, raw_ops)
512+
513+
@_get_opts.register
514+
def _(op: schema_fb.OperatorT):
515+
if is_opt_1_type:
516+
raw_ops_t = op.builtinOptions
517+
else:
518+
raw_ops_t = op.builtinOptions2
519+
if raw_ops_t is None or not isinstance(raw_ops_t, opts_type):
520+
return None
521+
return raw_ops_t
522+
523+
return _get_opts(op)

0 commit comments

Comments
 (0)