chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
+593
View File
@@ -0,0 +1,593 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite:build_def.bzl", "tflite_copts", "tflite_copts_warnings", "tflite_self_contained_libs_test_suite")
load("//tensorflow/lite:special_rules.bzl", "internal_visibility_allowlist", "tflite_portable_test_suite")
load("//tensorflow/lite/core:special_rules.bzl", "core_cc_api_stable_visibility_allowlist", "macros_visibility_allowlist")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
licenses = ["notice"],
)
exports_files(
srcs = [
"create_op_resolver.h",
"macros.h",
"subgraph.h",
],
visibility = [
"//tensorflow/lite:__subpackages__",
],
)
bzl_library(
name = "special_rules_bzl",
srcs = ["special_rules.bzl"],
visibility = ["//tensorflow/lite:__subpackages__"],
)
# The public target for the C++ API excluding experimental APIs.
# TODO(ahentz): investigate dependency on gemm_support requiring usage of tf_copts.
cc_library(
name = "framework_stable",
srcs = [
"subgraph.h",
],
hdrs = [
"interpreter.h",
"interpreter_builder.h",
"macros.h",
"model.h",
"model_builder.h",
"signature_runner.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = ["//tensorflow/lite:__subpackages__"],
deps = [
":cc_api_stable",
":signature_runner",
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/compiler/mlir/lite/experimental/remat:metadata_util",
"//tensorflow/lite:allocation",
"//tensorflow/lite:array",
"//tensorflow/lite:external_cpu_backend_context",
"//tensorflow/lite:graph_info",
"//tensorflow/lite:interpreter_options_header",
"//tensorflow/lite:macros",
"//tensorflow/lite:memory_planner",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:string",
"//tensorflow/lite:type_to_tflitetype",
"//tensorflow/lite:util",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/async:async_signature_runner",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/experimental/resource",
"//tensorflow/lite/internal:signature_def",
"//tensorflow/lite/profiling:root_profiler",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting_internal",
"//tensorflow/lite/schema:schema_fbs",
"@flatbuffers//:runtime_cc",
],
)
# The public target for the full C++ API, including experimental APIs.
#
# Experimental APIs are functional, tested and usable in production; however,
# the corresponding API surface has not been finalized, and is subject to
# change.
alias(
name = "framework",
actual = "framework_experimental",
compatible_with = get_compatible_with_portable(),
visibility = ["//visibility:public"],
)
# The full C++ API, including experimental APIs.
#
# Experimental APIs are functional, tested and usable in production; however,
# the corresponding API surface has not been finalized, and is subject to
# change.
#
# Note that if you have code which depends on both stable and experimental API
# features, it's fine to depend only on 'framework_experimental', since
# that includes 'framework_stable' as a subset.
cc_library(
name = "framework_experimental",
srcs = [],
hdrs = [
"interpreter.h",
"interpreter_builder.h",
"macros.h",
"model.h",
"model_builder.h",
"subgraph.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
":cc_api_experimental",
":cc_api_stable",
":model_builder",
":signature_runner",
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/compiler/mlir/lite/experimental/remat:metadata_util",
"//tensorflow/lite:allocation",
"//tensorflow/lite:array",
"//tensorflow/lite:external_cpu_backend_context",
"//tensorflow/lite:graph_info",
"//tensorflow/lite:interpreter_options_header",
"//tensorflow/lite:macros",
"//tensorflow/lite:memory_planner",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:string",
"//tensorflow/lite:type_to_tflitetype",
"//tensorflow/lite:util",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/async:async_signature_runner",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/experimental/resource",
"//tensorflow/lite/internal:signature_def",
"//tensorflow/lite/profiling:root_profiler",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting_internal",
"//tensorflow/lite/schema:schema_fbs",
"@flatbuffers//:runtime_cc",
],
alwayslink = 1, # TODO(b/161243354): eliminate this.
)
# This is a private target, its visibility is set to public only to be
# used by LiteRT dependencies.
# Do not use this target directly and don't consider it as a part of the public API.
# TODO(weiyiw): Refactor LiteRT deps from TFLite.
alias(
name = "private_cc_api_stable",
actual = ":cc_api_stable",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
# TODO(b/242310498): move logger.cc from tensorflow/lite/ to here.
cc_library(
name = "cc_api_stable",
srcs = [
"interpreter.cc",
"interpreter_builder.cc",
"subgraph.h",
],
hdrs = [
"interpreter.h",
"interpreter_builder.h",
"model.h",
"model_builder.h",
"signature_runner.h",
],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
"//third_party/deepmind/lyria_live/internal/odml:__subpackages__",
"//third_party/odml/litert:__subpackages__",
] + core_cc_api_stable_visibility_allowlist(),
deps = [
":model_builder",
":signature_runner",
":subgraph",
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/compiler/mlir/lite/experimental/remat:metadata_util",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/compiler/mlir/lite/schema:schema_utils",
"//tensorflow/lite:allocation",
"//tensorflow/lite:array",
"//tensorflow/lite:external_cpu_backend_context",
"//tensorflow/lite:graph_info",
"//tensorflow/lite:interpreter_options_header",
"//tensorflow/lite:macros",
"//tensorflow/lite:memory_planner",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:shared_library",
"//tensorflow/lite:simple_memory_arena",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:string",
"//tensorflow/lite:tensorflow_profiler_logger_shim",
"//tensorflow/lite:type_to_tflitetype",
"//tensorflow/lite:util",
"//tensorflow/lite:version",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/api:verifier",
"//tensorflow/lite/core/async:async_signature_runner",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
# copybara:uncomment "//tensorflow/lite/delegates:telemetry",
"//tensorflow/lite/delegates/xnnpack:tflite_with_xnnpack_qs8",
"//tensorflow/lite/delegates/xnnpack:tflite_with_xnnpack_qu8",
"//tensorflow/lite/experimental/resource",
"//tensorflow/lite/internal:signature_def",
"//tensorflow/lite/kernels/internal:compatibility",
"//tensorflow/lite/profiling:platform_profiler",
"//tensorflow/lite/profiling:root_profiler",
"//tensorflow/lite/profiling/telemetry",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting_internal",
"//tensorflow/lite/schema:conversion_metadata_fbs",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/schema:schema_utils",
"@flatbuffers//:runtime_cc",
"@ruy//ruy:denormal",
],
alwayslink = 1,
)
# The key parts of the C++ API. This target defines the TF Lite classes for
# loading models and interpreting them.
# DEPRECATED: prefer to depend on :cc_api_stable or :cc_api_experimental.
alias(
name = "cc_api",
actual = "cc_api_experimental",
visibility = ["//tensorflow/lite:__subpackages__"],
)
# The key parts of the C++ API, including experimental APIs.
#
# This target has restricted visibility; for a public target that exposes
# these APIs, see 'framework_experimental' above.
cc_library(
name = "cc_api_experimental",
srcs = [
"interpreter_experimental.cc",
],
hdrs = [
"interpreter.h",
"interpreter_builder.h",
"model.h",
"model_builder.h",
"signature_runner.h",
"subgraph.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
":cc_api_stable",
":signature_runner",
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/compiler/mlir/lite/experimental/remat:metadata_util",
"//tensorflow/lite:allocation",
"//tensorflow/lite:array",
"//tensorflow/lite:external_cpu_backend_context",
"//tensorflow/lite:graph_info",
"//tensorflow/lite:interpreter_options_header",
"//tensorflow/lite:macros",
"//tensorflow/lite:memory_planner",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:string",
"//tensorflow/lite:type_to_tflitetype",
"//tensorflow/lite:util",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/async:async_signature_runner",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/experimental/resource",
"//tensorflow/lite/internal:signature_def",
"//tensorflow/lite/profiling:root_profiler",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting",
"//tensorflow/lite/profiling/telemetry/c:telemetry_setting_internal",
"//tensorflow/lite/schema:schema_fbs",
"@flatbuffers//:runtime_cc",
],
alwayslink = 1, # TODO(b/161243354): eliminate this.
)
cc_library(
name = "model_builder",
hdrs = ["model_builder.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts_warnings(),
visibility = internal_visibility_allowlist(),
deps = [
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite/core/api:error_reporter",
],
alwayslink = 1,
)
cc_library(
name = "signature_runner",
srcs = ["signature_runner.cc"],
hdrs = ["signature_runner.h"],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__pkg__",
"//tensorflow/lite/core:__subpackages__",
"//third_party/odml/infra/genai/inference/executor/google_tensor:__subpackages__",
],
deps = [
"//tensorflow/lite/c:common",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/internal:signature_def",
],
)
# Test signature runner.
cc_test(
name = "signature_runner_test",
size = "small",
srcs = ["signature_runner_test.cc"],
data = [
"//tensorflow/lite:testdata/multi_signatures.bin",
"//tensorflow/lite:testdata/no_signatures.bin",
"//tensorflow/lite:testdata/no_signatures_no_tensor_names.bin",
"//tensorflow/lite:testdata/reverse_signature_model.bin",
],
deps = [
":framework",
":signature_runner",
"//tensorflow/compiler/mlir/lite/core:model_builder_base",
"//tensorflow/lite:model_builder",
"//tensorflow/lite/core/kernels:builtin_ops",
"//tensorflow/lite/testing:util",
"@com_google_googletest//:gtest_main",
],
)
# Test model framework.
cc_test(
name = "model_test",
size = "small",
srcs = ["model_test.cc"],
data = [
"//tensorflow/lite:testdata/0_subgraphs.bin",
"//tensorflow/lite:testdata/2_subgraphs.bin",
"//tensorflow/lite:testdata/2_subgraphs_dont_delegate_name.bin",
"//tensorflow/lite:testdata/add_shared_tensors.bin",
"//tensorflow/lite:testdata/empty_model.bin",
"//tensorflow/lite:testdata/multi_add_flex.bin",
"//tensorflow/lite:testdata/segment_sum_invalid_buffer.bin",
"//tensorflow/lite:testdata/sparse_tensor.bin",
"//tensorflow/lite:testdata/test_min_runtime.bin",
"//tensorflow/lite:testdata/test_model.bin",
"//tensorflow/lite:testdata/test_model_broken.bin",
"//tensorflow/lite:testdata/test_model_redux_precision.bin",
"//tensorflow/lite:testdata/while_op_with_forwarding_input.bin",
"//tensorflow/lite:testdata/zero_size_constant.bin",
],
tags = [
"no_windows", # TODO(b/194459105): the test is flaky.
"noasan",
"tflite_not_portable",
"tflite_smoke_test",
],
deps = [
":framework",
"//tensorflow/compiler/mlir/lite:allocation",
"//tensorflow/lite:framework",
"//tensorflow/lite:string_util",
"//tensorflow/lite:version",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/api:verifier",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/core/kernels:builtin_ops",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/testing:util",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
],
)
cc_library(
name = "create_op_resolver_header",
hdrs = [
"create_op_resolver.h",
],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:op_resolver",
],
)
# Defines CreateOpResolver with all builtin ops.
cc_library(
name = "create_op_resolver_with_builtin_ops",
srcs = ["create_op_resolver_with_builtin_ops.cc"],
hdrs = ["create_op_resolver.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:op_resolver",
"//tensorflow/lite/core/kernels:builtin_ops",
],
# Some targets only have an implicit dependency on CreateOpResolver.
# This avoids warnings about backwards references when linking.
alwayslink = True,
)
# This target is only for use by the "tflite_custom_c_library" and "tflite_custom_cc_library" build
# macro and should not be used anywhere other than in the implementation of that build macro.
# "tflite_custom_c_library" requires target to be public, that's why we duplicated
# :create_op_resolver_header target to be used only by "tflite_custom_c_library".
# Making :create_op_resolver_header public could cause some problems because it is widely used
# inside the TF Lite code base, that might lead others outside the TF Lite code base to copy that
# dependency and use it and subsequently depend on it, which would be bad. Using a separate
# :private_create_op_resolver_header target ensures that the only use of the unwantedly-"public"
# target is inside the "tflite_custom_c_library" itself, where it is less likely to get copied into
# third party code.
alias(
name = "private_create_op_resolver_header",
actual = ":create_op_resolver_header",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
cc_library(
name = "macros",
hdrs = ["macros.h"],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + macros_visibility_allowlist(),
)
cc_library(
name = "subgraph",
srcs = [
"subgraph.cc",
],
hdrs = [
"subgraph.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
"//tensorflow/lite:__subpackages__",
"//tensorflow/lite/core:__subpackages__",
"//tensorflow/lite/kernels:__subpackages__",
],
deps = [
"//tensorflow/compiler/mlir/lite/experimental/remat:metadata_util",
"//tensorflow/lite:allocation",
"//tensorflow/lite:array",
"//tensorflow/lite:graph_info",
"//tensorflow/lite:interpreter_options_header",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:macros",
"//tensorflow/lite:memory_planner",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite:util",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/experimental/resource",
"//tensorflow/lite/profiling:root_profiler",
"//tensorflow/lite/profiling/telemetry",
"//tensorflow/lite/schema:schema_fbs",
] + select({
"//tensorflow/lite:tflite_use_simple_memory_planner": [
"//tensorflow/lite:simple_planner",
],
"//conditions:default": [
"//tensorflow/lite:arena_planner",
],
}) + select({
"//tensorflow/lite:tensorflow_profiler_config": [
"//tensorflow/lite:tensorflow_profiler_logger_shim",
],
"//conditions:default": [],
}),
alwayslink = 1, # TODO(b/161243354): eliminate this.
)
# Test subgraph.
cc_test(
name = "subgraph_test",
size = "small",
srcs = [
"subgraph_test.cc",
],
deps = [
":framework_stable",
"//tensorflow/lite:framework",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/kernels:builtin_ops", # build_cleaner: keep
"@com_google_absl//absl/log:check",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "model_building",
srcs = ["model_building.cc"],
hdrs = ["model_building.h"],
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
":subgraph",
"//tensorflow/lite:array",
"//tensorflow/lite:framework",
"//tensorflow/lite:type_to_tflitetype",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/kernels:builtin_ops",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_absl//absl/container:flat_hash_map",
],
)
cc_test(
name = "model_building_test",
srcs = ["model_building_test.cc"],
deps = [
":model_building",
"//tensorflow/lite:framework",
"//tensorflow/lite/c:c_api_types",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "subgraph_composite_inlining_test",
srcs = ["subgraph_composite_inlining_test.cc"],
deps = [
":model_building",
":subgraph",
"//tensorflow/lite:array",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:framework",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/kernels:kernel_util",
"//tensorflow/lite/kernels:subgraph_test_util",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/kernels/internal:tensor_ctypes",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
],
)
tflite_self_contained_libs_test_suite(name = "self_contained_libs_test_suite")
tflite_portable_test_suite()
+28
View File
@@ -0,0 +1,28 @@
This directory contains the "core" part of the TensorFlow Lite runtime library.
The header files in this `tensorflow/lite/core/` directory fall into several
categories.
1. Public API headers, in the `api` subdirectory `tensorflow/lite/core/api/`
These are in addition to the other public API headers in `tensorflow/lite/`.
For example:
- `tensorflow/lite/core/api/error_reporter.h`
- `tensorflow/lite/core/api/op_resolver.h`
2. Private headers that define public API types and functions.
These headers are each `#include`d from a corresponding public "shim" header
in `tensorflow/lite/` that forwards to the private header.
For example:
- `tensorflow/lite/core/interpreter.h` is a private header file that is
included from the public "shim" header file `tensorflow/lite/interpeter.h`.
These private header files should be used as follows: `#include`s from `.cc`
files in TF Lite itself that are _implementing_ the TF Lite APIs should
include the "core" TF Lite API headers. `#include`s from files that are
just _using_ the regular TF Lite APIs should include the regular public
headers.
3. The header file `tensorflow/lite/core/subgraph.h`. This contains
some experimental APIs.
@@ -0,0 +1,133 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite:special_rules.bzl", "nnapi_plugin_impl_visibility_allowlist", "xnnpack_plugin_impl_visibility_allowlist")
load("//tensorflow/lite/core:special_rules.bzl", "delegate_registry_visibility_allowlist")
load("//tensorflow/lite/core/c:special_rules.bzl", "experimental_acceleration_api_allowlist")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
licenses = ["notice"],
)
cc_library(
name = "delegate_registry",
srcs = ["delegate_registry.cc"],
hdrs = ["delegate_registry.h"],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + delegate_registry_visibility_allowlist(),
deps = [
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/c:common",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/synchronization",
],
)
cc_library(
name = "nnapi_plugin",
srcs = ["nnapi_plugin.cc"],
hdrs = ["nnapi_plugin.h"],
compatible_with = get_compatible_with_portable(),
visibility = nnapi_plugin_impl_visibility_allowlist() + [
"//tensorflow/lite:__subpackages__",
],
deps = [
":delegate_registry",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/acceleration/configuration/c:delegate_plugin",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/nnapi:nnapi_implementation_headers",
"//tensorflow/lite/nnapi:nnapi_lib",
"@com_google_absl//absl/memory",
],
alwayslink = 1, # For registration to always run.
)
cc_test(
name = "nnapi_plugin_test",
srcs = ["nnapi_plugin_test.cc"],
tags = [
"no_mac",
"no_windows",
"tflite_not_portable_ios",
],
deps = [
":delegate_registry",
":nnapi_plugin",
"//tensorflow/lite:framework",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core:framework",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate_mock_test",
"//tensorflow/lite/kernels:test_util",
"//tensorflow/lite/nnapi:nnapi_implementation_headers",
"//tensorflow/lite/nnapi:nnapi_lib",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
"@flatbuffers",
],
)
cc_library(
name = "stable_delegate_registry",
srcs = ["stable_delegate_registry.cc"],
hdrs = ["stable_delegate_registry.h"],
visibility = [
"//tensorflow/lite:__subpackages__",
] + experimental_acceleration_api_allowlist(),
deps = [
"//tensorflow/lite/core/acceleration/configuration/c:stable_delegate",
"//tensorflow/lite/core/shims:tflite_use_opaque_delegate", # buildcleaner: keep
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/synchronization",
],
)
cc_test(
name = "stable_delegate_registry_test",
srcs = ["stable_delegate_registry_test.cc"],
deps = [
":stable_delegate_registry",
"//tensorflow/lite/core/acceleration/configuration/c:stable_delegate",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "xnnpack_plugin",
srcs = ["xnnpack_plugin.cc"],
compatible_with = get_compatible_with_portable(),
visibility = xnnpack_plugin_impl_visibility_allowlist() + [
"//tensorflow/lite:__subpackages__",
],
deps = [
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/core/acceleration/configuration:delegate_registry",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"@com_google_absl//absl/base:log_severity",
"@com_google_absl//absl/memory",
],
alwayslink = 1, # For registration to always run.
)
cc_test(
name = "xnnpack_plugin_test",
srcs = ["xnnpack_plugin_test.cc"],
deps = [
":xnnpack_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/acceleration/configuration:delegate_registry",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
"@pthreadpool",
],
)
@@ -0,0 +1,244 @@
# 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.
# ==============================================================================
# C API for delegate plugins.
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load(
"//tensorflow/core/platform:build_config_root.bzl",
"tf_gpu_tests_tags",
)
load("//tensorflow/lite:build_def.bzl", "tflite_cc_library_with_c_headers_test", "tflite_copts", "tflite_copts_warnings")
load(
"//tensorflow/lite/core/acceleration/configuration/c:special_rules.bzl",
"delegate_plugin_visibility_allowlist",
"gpu_plugin_visibility_allowlist",
"xnnpack_plugin_visibility_allowlist",
)
load("//tensorflow/lite/core/c:special_rules.bzl", "experimental_acceleration_api_allowlist")
load("//tensorflow/lite/core/shims:build_defs.bzl", "build_test")
load("//tensorflow/lite/delegates/gpu:build_defs.bzl", "gpu_delegate_linkopts")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//tensorflow/lite:__subpackages__",
] + experimental_acceleration_api_allowlist(),
licenses = ["notice"],
)
filegroup(
name = "tflite_internal_cc_3p_api_deps_src",
srcs = [
"stable_delegate.h",
],
visibility = [
"//tensorflow/lite:__pkg__",
],
)
# LINT.IfChange(tflite_acceleration_exported_headers)
exports_files([
"delegate_plugin.h",
"gpu_plugin.h",
"xnnpack_plugin.h",
])
# LINT.ThenChange(
# ../../../../acceleration/configuration/c/BUILD:tflite_acceleration_exported_headers,
# ../../../../java/BUILD:tflite_acceleration_exported_headers
# )
tflite_cc_library_with_c_headers_test(
name = "delegate_plugin",
hdrs = ["delegate_plugin.h"],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + delegate_plugin_visibility_allowlist(),
deps = [
"//tensorflow/lite/core/c:common",
],
)
common_copts = tflite_copts() + tflite_copts_warnings()
tflite_cc_library_with_c_headers_test(
name = "gpu_plugin",
srcs = ["gpu_plugin.cc"],
hdrs = ["gpu_plugin.h"],
copts = common_copts + select({
"//tensorflow:ios": [
"-xobjective-c++",
],
"//tensorflow:macos_arm64": [
"-xobjective-c++",
],
"//conditions:default": [],
}),
visibility = [
"//tensorflow/lite:__subpackages__",
] + gpu_plugin_visibility_allowlist(),
deps = [
":delegate_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/acceleration/configuration:gpu_plugin_impl",
"//tensorflow/lite/core/c:common",
] + select({
"//tensorflow/lite/delegates/gpu:supports_gpu_delegate": [
"//tensorflow/lite/delegates/gpu:delegate",
],
"//conditions:default": [],
}) + select({
"//tensorflow:ios": [
"//tensorflow/lite/delegates/gpu:metal_delegate",
],
"//tensorflow:macos_arm64": [
"//tensorflow/lite/delegates/gpu:metal_delegate",
],
"//conditions:default": [],
}),
)
# For non-Android platforms, this should be built with '--copt=-DCL_DELEGATE_NO_GL'.
# On non-supported platforms (i.e. non-Android platforms if -DCL_DELEGATE_NO_GL wasn't specified),
# the test srcs are set to the empty list, so the test will succeed without testing anything.
cc_test(
name = "gpu_plugin_test",
srcs = select({
"//tensorflow/lite/delegates/gpu:supports_gpu_delegate": ["gpu_plugin_test.cc"],
"//conditions:default": [],
}),
linkopts = gpu_delegate_linkopts(),
tags = tf_gpu_tests_tags(),
deps = select({
"//tensorflow/lite/delegates/gpu:supports_gpu_delegate": [":gpu_plugin"],
"//conditions:default": [],
}) + [
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/c:common",
"@com_google_googletest//:gtest_main",
],
)
tflite_cc_library_with_c_headers_test(
name = "nnapi_plugin",
srcs = ["nnapi_plugin.cc"],
hdrs = ["nnapi_plugin.h"],
deps = [
":delegate_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/acceleration/configuration:nnapi_plugin",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
],
)
cc_test(
name = "nnapi_plugin_test",
srcs = ["nnapi_plugin_test.cc"],
deps = [
":nnapi_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/c:common",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
],
)
tflite_cc_library_with_c_headers_test(
name = "xnnpack_plugin",
srcs = ["xnnpack_plugin.cc"],
hdrs = ["xnnpack_plugin.h"],
compatible_with = get_compatible_with_portable(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + xnnpack_plugin_visibility_allowlist(),
deps = [
":delegate_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
],
)
cc_test(
name = "xnnpack_plugin_test",
srcs = ["xnnpack_plugin_test.cc"],
deps = [
":xnnpack_plugin",
"//tensorflow/lite/acceleration/configuration:configuration_fbs",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates/xnnpack:xnnpack_delegate",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
"@pthreadpool",
],
)
tflite_cc_library_with_c_headers_test(
name = "stable_delegate",
hdrs = ["stable_delegate.h"],
compatible_with = get_compatible_with_portable(),
deps = [
"//tensorflow/lite/core/acceleration/configuration/c:delegate_plugin",
],
)
# Commented out under the (b/279852433) because caused an error in the OSS
# TODO(zhurakovskyi): Uncomment when fixed.
#
# copybara:uncomment_begin
# # This rule invokes the "flatcc" FlatBuffer C API compiler to generate the sources
# # use by the ":configuration_c_fbs" C library rule below.
# genrule(
# name = "configuration_c_fbs_gen",
# srcs = ["//tensorflow/lite/acceleration/configuration:configuration.fbs"],
# outs = [
# "configuration_builder.h",
# "configuration_reader.h",
# ],
# cmd = "$(location //third_party/flatcc:flatcc) -o$(RULEDIR) --builder --reader $(SRCS)",
# tools = ["//third_party/flatcc"],
# # Currently this only enables the API for _building_ configuration flatbuffer objects,
# # not the APIs for reading them, verifying them, or converting them to/from JSON.
# # [If you need to enable those, replace the two lines above with the following
# # outs = ["configuration_builder.h", "configuration_reader.h", "configuration_verifier.h",
# # "configuration_json_parser.h", "configuration_json_printer.h"],
# # cmd = "$(location //third_party/flatcc:flatcc) -o$(RULEDIR) " +
# # "--builder --reader --verifier --json $(SRCS)",
# # and then in the rule below -- or preferably in a separate target --
# # add the additional header files in "hdrs" and fix the dependencies.]
# )
#
# # This rule defines a C library containing the Flatbuffer-generated C API for constructing objects
# # using the FlatBuffer schema generated from tensorflow/lite/acceleration/configuration/configuration.proto,
# # which defines the 'TFLiteSettings' FlatBuffer table and related types.
# tflite_cc_library_with_c_headers_test(
# name = "configuration_c_fbs",
# hdrs = [
# "configuration_builder.h",
# "configuration_reader.h",
# ],
# deps = ["//third_party/flatcc:runtime"],
# )
#
# build_test(
# name = "configuration_c_fbs_build_test",
# targets = [
# ":configuration_c_fbs",
# ],
# )
# copybara:uncomment_end
@@ -0,0 +1,144 @@
/* 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.
==============================================================================*/
// WARNING: Users of TensorFlow Lite should not include this file directly,
// but should instead include
// "third_party/tensorflow/lite/acceleration/configuration/c/delegate_plugin.h".
// Only the TensorFlow Lite implementation itself should include this file
// directly.
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_DELEGATE_PLUGIN_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_DELEGATE_PLUGIN_H_
/// C API types for TF Lite delegate plugins.
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/// \note Users of TensorFlow Lite should use
/// \code
/// #include "tensorflow/lite/acceleration/configuration/c/delegate_plugin.h"
/// \endcode
/// to access the APIs documented on this page.
// NOLINTEND(whitespace/line_length)
// clang-format on
#include "tensorflow/lite/core/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/** \defgroup delegate_plugin lite/acceleration/configuration/c/delegate_plugin.h
* @{
*/
// NOLINTEND(whitespace/line_length)
// clang-format on
/// Type of delegate creation function used to allocate and construct a
/// delegate.
///
/// The `tflite_settings` parameter passed to the delegate creation function
/// should be a pointer to a FlatBuffer table object of type
/// `tflite::TFLiteSettings`. We use `const void *` here rather than `const
/// tflite::TFLiteSettings*` since this is a C API so we don't want to directly
/// reference C++ types such as `tflite::TFLiteSettings`. But note that this
/// address should point to the 'parsed' FlatBuffer object, not the raw byte
/// buffer. (Note that 'parsing' FlatBuffers is very cheap, it's just an offset
/// load.)
///
/// If you are using the FlatBuffers C API, then you can alternatively pass
/// in a value of type `tflite_TFLiteSettings_table_t`, which is a typedef for
/// `const struct tflite_TFLiteSettings_table*` -- that is the corresponding
/// type for the 'parsed' FlatBuffer object in the FlatBuffers C API.
///
/// Ownership of the `tflite_settings` flatbuffer remains with the caller.
/// The caller of a delegate creation function may end the lifetime of the
/// `tflite_settings` FlatBuffer immediately after the call to the function.
/// So the delegate creation function should ensure that any settings that the
/// delegate may need to reference later, after the delegate has been
/// constructed, are copied from the FlatBuffer into storage owned by the
/// delegate.
typedef TfLiteDelegate *TfLiteDelegatePluginCreateFunc(
const void *tflite_settings);
/// Type of function to destroy and deallocate a delegate.
/// The delegate argument must have been created with the corresponding
/// create function from the same delegate plugin.
typedef void TfLiteDelegatePluginDestroyFunc(TfLiteDelegate *);
/// Type of function to return an error code for the last delegate operation.
/// The delegate argument must have been created with the corresponding
/// create function from the same delegate plugin.
typedef int TfLiteDelegatePluginGetDelegateErrnoFunc(TfLiteDelegate *);
/// Struct to hold all the methods for a delegate plugin.
typedef struct TfLiteDelegatePlugin {
/// Function to allocate and construct a delegate.
TfLiteDelegatePluginCreateFunc *create;
/// Function to deallocate a delegate.
TfLiteDelegatePluginDestroyFunc *destroy;
/// Function to return an error code for the last delegate operation.
TfLiteDelegatePluginGetDelegateErrnoFunc *get_delegate_errno;
} TfLiteDelegatePlugin;
// The following block guarded by TFLITE_USE_OPAQUE_DELEGATE has the exact same
// functionality as the concrete types above but only uses truly opaque types.
// Note that it has to be an addition along with the concrete types at this
// point because the in some cases both types are used together in a same build
// target. e.g. TFLite-in-Play Services initialization context.
#if TFLITE_USE_OPAQUE_DELEGATE
/// Same as TfLiteDelegatePluginCreateFunc but uses truly opaque types.
typedef TfLiteOpaqueDelegateStruct *TfLiteOpaqueDelegatePluginCreateFunc(
const void *tflite_settings);
/// Same as TfLiteDelegatePluginDestroyFunc but uses truly opaque types.
typedef void TfLiteOpaqueDelegatePluginDestroyFunc(
TfLiteOpaqueDelegateStruct *delegate);
/// Same as TfLiteDelegatePluginGetDelegateErrnoFunc but uses truly opaque
/// types.
typedef int TfLiteOpaqueDelegatePluginGetDelegateErrnoFunc(
TfLiteOpaqueDelegateStruct *delegate);
/// Same as TfLiteDelegatePlugin but uses truly opaque types.
typedef struct TfLiteOpaqueDelegatePlugin {
TfLiteOpaqueDelegatePluginCreateFunc *create;
TfLiteOpaqueDelegatePluginDestroyFunc *destroy;
TfLiteOpaqueDelegatePluginGetDelegateErrnoFunc *get_delegate_errno;
} TfLiteOpaqueDelegatePlugin;
#else
typedef TfLiteDelegatePluginCreateFunc TfLiteOpaqueDelegatePluginCreateFunc;
typedef TfLiteDelegatePluginDestroyFunc TfLiteOpaqueDelegatePluginDestroyFunc;
typedef TfLiteDelegatePluginGetDelegateErrnoFunc
TfLiteOpaqueDelegatePluginGetDelegateErrnoFunc;
typedef TfLiteDelegatePlugin TfLiteOpaqueDelegatePlugin;
#endif // TFLITE_USE_OPAQUE_DELEGATE
/** @} */
#ifdef __cplusplus
}; // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_DELEGATE_PLUGIN_H_
@@ -0,0 +1,68 @@
/* 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.
==============================================================================*/
// This file implements the Delegate Plugin for the GPU Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/gpu_plugin.h"
#include <memory>
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/acceleration/configuration/gpu_plugin.h"
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#include "tensorflow/lite/core/c/common.h"
#if TFLITE_SUPPORTS_GPU_DELEGATE
#include "tensorflow/lite/delegates/gpu/delegate.h"
#elif defined(REAL_IPHONE_DEVICE)
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
#endif
extern "C" {
static TfLiteDelegate* CreateDelegate(const void* settings) {
const ::tflite::TFLiteSettings* tflite_settings =
static_cast<const ::tflite::TFLiteSettings*>(settings);
tflite::delegates::GpuPlugin gpu_plugin(*tflite_settings);
#if TFLITE_SUPPORTS_GPU_DELEGATE
return TfLiteGpuDelegateV2Create(&gpu_plugin.Options());
#elif defined(REAL_IPHONE_DEVICE)
return TFLGpuDelegateCreate(&gpu_plugin.Options());
#else
return nullptr;
#endif
}
static void DestroyDelegate(TfLiteDelegate* delegate) {
#if TFLITE_SUPPORTS_GPU_DELEGATE
TfLiteGpuDelegateV2Delete(delegate);
#elif defined(REAL_IPHONE_DEVICE)
TFLGpuDelegateDelete(delegate);
#endif
}
static int DelegateErrno(TfLiteDelegate* from_delegate) { return 0; }
static constexpr TfLiteDelegatePlugin kPluginCApi{
CreateDelegate,
DestroyDelegate,
DelegateErrno,
};
const TfLiteDelegatePlugin* TfLiteGpuDelegatePluginCApi() {
return &kPluginCApi;
}
} // extern "C"
@@ -0,0 +1,69 @@
/* 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.
==============================================================================*/
// WARNING: Users of TensorFlow Lite should not include this file directly, but
// should instead include
// "third_party/tensorflow/lite/acceleration/configuration/c/gpu_plugin.h".
// Only the TensorFlow Lite implementation itself should include this file
// directly.
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_GPU_PLUGIN_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_GPU_PLUGIN_H_
/// This header file is for the delegate plugin for GPU.
///
/// For the C++ delegate plugin interface, the GPU delegate plugin is added to
/// the `DelegatePluginRegistry` by the side effect of a constructor for a
/// static object, so there's no public API needed for this plugin, other than
/// the API of `tflite::delegates::DelegatePluginRegistry`s, which is declared
/// in delegate_registry.h.
///
/// But to provide a C API to access the GPU delegate plugin, we do expose
/// some functions, which are declared below.
///
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/// \note Users of TensorFlow Lite should use
/// \code
/// #include "tensorflow/lite/acceleration/configuration/c/gpu_plugin.h"
/// \endcode
/// to access the APIs documented on this page.
// NOLINTEND(whitespace/line_length)
// clang-format on
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#ifdef __cplusplus
extern "C" {
#endif
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/** \defgroup gpu_plugin lite/acceleration/configuration/c/gpu_plugin.h
* @{
*/
// NOLINTEND(whitespace/line_length)
// clang-format on
/// C API for the GPU delegate plugin.
/// Returns a pointer to a statically allocated table of function pointers.
const TfLiteDelegatePlugin* TfLiteGpuDelegatePluginCApi();
/** @} */
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_GPU_PLUGIN_H_
@@ -0,0 +1,68 @@
/* 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.
==============================================================================*/
// Some very simple unit tests of the C API Delegate Plugin for the
// GPU Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/gpu_plugin.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
class GpuTest : public testing::Test {
public:
void SetUp() override {
// Construct a FlatBuffer that contains
// TFLiteSettings { GpuSettings { foo1 : bar1, foo2 : bar2, ...} }.
GPUSettingsBuilder gpu_settings_builder(flatbuffer_builder_);
flatbuffers::Offset<GPUSettings> gpu_settings =
gpu_settings_builder.Finish();
// gpu_settings_builder.add_foo1(bar1);
// gpu_settings_builder.add_foo2(bar2);
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_gpu_settings(gpu_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
}
~GpuTest() override {}
protected:
// settings_ points into storage owned by flatbuffer_builder_.
flatbuffers::FlatBufferBuilder flatbuffer_builder_;
const TFLiteSettings *settings_;
};
TEST_F(GpuTest, CanCreateAndDestroyDelegate) {
TfLiteDelegate *delegate = TfLiteGpuDelegatePluginCApi()->create(settings_);
EXPECT_NE(delegate, nullptr);
TfLiteGpuDelegatePluginCApi()->destroy(delegate);
}
TEST_F(GpuTest, CanGetDelegateErrno) {
TfLiteDelegate *delegate = TfLiteGpuDelegatePluginCApi()->create(settings_);
int error_number =
TfLiteGpuDelegatePluginCApi()->get_delegate_errno(delegate);
EXPECT_EQ(error_number, 0);
TfLiteGpuDelegatePluginCApi()->destroy(delegate);
}
} // namespace tflite
@@ -0,0 +1,62 @@
/* 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.
==============================================================================*/
// This file implements the Delegate Plugin for the NNAPI Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/nnapi_plugin.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#include "tensorflow/lite/core/acceleration/configuration/nnapi_plugin.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
extern "C" {
static TfLiteDelegate* CreateDelegate(const void* settings) {
const ::tflite::TFLiteSettings* tflite_settings =
static_cast<const ::tflite::TFLiteSettings*>(settings);
tflite::delegates::NnapiPlugin nnapi_plugin(*tflite_settings);
auto support_library_handle = nnapi_plugin.GetSupportLibraryHandle();
if (support_library_handle) {
auto nnapi_support_library_driver =
reinterpret_cast<const NnApiSLDriverImplFL5*>(support_library_handle);
return new tflite::StatefulNnApiDelegate(nnapi_support_library_driver,
nnapi_plugin.Options());
}
return new tflite::StatefulNnApiDelegate(nnapi_plugin.Options());
}
static void DestroyDelegate(TfLiteDelegate* delegate) {
delete static_cast<tflite::StatefulNnApiDelegate*>(delegate);
}
static int DelegateErrno(TfLiteDelegate* from_delegate) {
auto nnapi_delegate =
static_cast<tflite::StatefulNnApiDelegate*>(from_delegate);
return nnapi_delegate->GetNnApiErrno();
}
static constexpr TfLiteDelegatePlugin kPluginCApi{
CreateDelegate,
DestroyDelegate,
DelegateErrno,
};
const TfLiteDelegatePlugin* TfLiteNnapiDelegatePluginCApi() {
return &kPluginCApi;
}
} // extern "C"
@@ -0,0 +1,50 @@
/* 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.
==============================================================================*/
// NOLINTBEGIN(whitespace/line_length)
/// WARNING: Users of TensorFlow Lite should not include this file directly,
/// but should instead include
/// "third_party/tensorflow/lite/acceleration/configuration/c/nnapi_plugin.h".
/// Only the TensorFlow Lite implementation itself should include this
/// file directly.
// NOLINTEND(whitespace/line_length)
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_NNAPI_PLUGIN_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_NNAPI_PLUGIN_H_
// This header file is for the delegate plugin for NNAPI.
//
// For the C++ delegate plugin interface, the NNAPI delegate plugin is added to
// the DelegatePluginRegistry by the side effect of a constructor for a static
// object, so there's no public API needed for this plugin, other than the API
// of tflite::delegates::DelegatePluginRegistry, which is declared in
// delegate_registry.h.
//
// But to provide a C API to access the NNAPI delegate plugin, we do expose
// some functions, which are declared below.
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#ifdef __cplusplus
extern "C" {
#endif
// C API for the NNAPI delegate plugin.
// Returns a pointer to a statically allocated table of function pointers.
const TfLiteDelegatePlugin* TfLiteNnapiDelegatePluginCApi();
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_NNAPI_PLUGIN_H_
@@ -0,0 +1,69 @@
/* 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.
==============================================================================*/
// Some very simple unit tests of the C API Delegate Plugin for the
// NNAPI Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/nnapi_plugin.h"
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
class NnapiTest : public testing::Test {
public:
void SetUp() override {
// Construct a FlatBuffer that contains
// TFLiteSettings { NnapiSettings { foo1 : bar1, foo2 : bar2, ...} }.
NNAPISettingsBuilder nnapi_settings_builder(flatbuffer_builder_);
flatbuffers::Offset<NNAPISettings> nnapi_settings =
nnapi_settings_builder.Finish();
// nnapi_settings_builder.add_foo1(bar1);
// nnapi_settings_builder.add_foo2(bar2);
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
}
~NnapiTest() override {}
protected:
// settings_ points into storage owned by flatbuffer_builder_.
flatbuffers::FlatBufferBuilder flatbuffer_builder_;
const TFLiteSettings *settings_;
};
TEST_F(NnapiTest, CanCreateAndDestroyDelegate) {
TfLiteDelegate *delegate = TfLiteNnapiDelegatePluginCApi()->create(settings_);
EXPECT_NE(delegate, nullptr);
TfLiteNnapiDelegatePluginCApi()->destroy(delegate);
}
TEST_F(NnapiTest, CanGetDelegateErrno) {
TfLiteDelegate *delegate = TfLiteNnapiDelegatePluginCApi()->create(settings_);
int error_number =
TfLiteNnapiDelegatePluginCApi()->get_delegate_errno(delegate);
EXPECT_EQ(error_number, 0);
TfLiteNnapiDelegatePluginCApi()->destroy(delegate);
}
} // namespace tflite
@@ -0,0 +1,31 @@
# 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.
# ==============================================================================
"""External-only build rules for delegate plugins."""
def delegate_plugin_visibility_allowlist():
"""Returns a list of packages that can depend on delegate_plugin."""
return []
def gpu_plugin_visibility_allowlist():
"""Returns a list of packages that can depend on gpu_plugin."""
return []
def xnnpack_plugin_visibility_allowlist():
"""Returns a list of packages that can depend on xnnpack_plugin."""
return []
def stable_delegate_visibility_allowlist():
"""Returns a list of packages that can depend on stable_delegate."""
return []
@@ -0,0 +1,54 @@
/* 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_CORE_ACCELERATION_CONFIGURATION_C_STABLE_DELEGATE_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_STABLE_DELEGATE_H_
// C API types for TFLite delegates that implement stable delegate ABI.
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#ifdef __cplusplus
extern "C" {
#endif
// Constant that identifies the TfLiteStableDelegate ABI version that the
// delegate supports. This will get incremented if there are changes to the
// struct. The version is in semver 2 format (see https://semver.org).
#define TFL_STABLE_DELEGATE_ABI_VERSION "1.0.0"
// Contains stable delegate metadata and implementation.
typedef struct TfLiteStableDelegate {
// The struct ABI version this delegate supports in semver 2 format. It should
// be set to TFL_STABLE_DELEGATE_ABI_VERSION.
const char* delegate_abi_version;
// Uniquely identifies a delegate.
// Format: {vendor}_{delegate}. Prefer using snake_case.
// e.g. "mycompany_gpu_delegate"
const char* delegate_name;
// Release version of this delegate.
// Prefer using semver 2 format.
const char* delegate_version;
// Provides the implementation of the delegate plugin.
const TfLiteOpaqueDelegatePlugin* delegate_plugin;
} TfLiteStableDelegate;
#ifdef __cplusplus
}; // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_STABLE_DELEGATE_H_
@@ -0,0 +1,69 @@
/* 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.
==============================================================================*/
// This file implements the C API Delegate Plugin for the XNNPACK Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/xnnpack_plugin.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
extern "C" {
static TfLiteDelegate* CreateDelegate(const void* settings) {
const ::tflite::TFLiteSettings* tflite_settings =
static_cast<const ::tflite::TFLiteSettings*>(settings);
auto options(TfLiteXNNPackDelegateOptionsDefault());
// The following code block is duplicated in the C++ XNNPack delegate plugin.
// LINT.IfChange(tflite_settings_to_xnnpack_delegate_options)
const auto* xnnpack_settings = tflite_settings->xnnpack_settings();
if (xnnpack_settings) {
options.num_threads = xnnpack_settings->num_threads();
// If xnnpack_settings->flags is zero, then leave options.flags
// unmodified, i.e. use the default flags (not zero).
// If xnnpack_settings->flags is nonzero, then use exactly
// those flags (i.e. discard the default flags).
if (xnnpack_settings->flags()) {
options.flags = xnnpack_settings->flags();
}
options.runtime_flags = xnnpack_settings->runtime_flags();
if (xnnpack_settings->weight_cache_file_path()) {
options.weight_cache_file_path =
xnnpack_settings->weight_cache_file_path()->c_str();
}
}
// LINT.ThenChange(../xnnpack_plugin.cc:tflite_settings_to_xnnpack_delegate_options)
return TfLiteXNNPackDelegateCreate(&options);
}
static void DestroyDelegate(TfLiteDelegate* delegate) {
TfLiteXNNPackDelegateDelete(delegate);
}
static int DelegateErrno(TfLiteDelegate* from_delegate) { return 0; }
static constexpr TfLiteDelegatePlugin kPluginCApi{
CreateDelegate,
DestroyDelegate,
DelegateErrno,
};
const TfLiteDelegatePlugin* TfLiteXnnpackDelegatePluginCApi() {
return &kPluginCApi;
}
} // extern "C"
@@ -0,0 +1,69 @@
/* 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.
==============================================================================*/
// WARNING: Users of TensorFlow Lite should not include this file directly, but
// should instead include
// "third_party/tensorflow/lite/acceleration/configuration/c/xnnpack_plugin.h".
// Only the TensorFlow Lite implementation itself should include this file
// directly.
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_H_
/// This header file is for the delegate plugin for XNNPACK.
///
/// For the C++ delegate plugin interface, the XNNPACK delegate plugin is added
/// to the DelegatePluginRegistry by the side effect of a constructor for a
/// static object, so there's no public API needed for this plugin, other than
/// the API of `tflite::delegates::DelegatePluginRegistry`, which is declared in
/// delegate_registry.h.
///
/// But to provide a C API to access the XNNPACK delegate plugin, we do expose
/// some functions, which are declared below.
///
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/// \note Users of TensorFlow Lite should use
/// \code
/// #include "tensorflow/lite/acceleration/configuration/c/xnnpack_plugin.h"
/// \endcode
/// to access the APIs documented on this page.
// NOLINTEND(whitespace/line_length)
// clang-format on
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#ifdef __cplusplus
extern "C" {
#endif
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/** \defgroup xnnpack_plugin lite/acceleration/configuration/c/xnnpack_plugin.h
* @{
*/
// NOLINTEND(whitespace/line_length)
// clang-format on
/// C API for the XNNPACK delegate plugin.
/// Returns a pointer to a statically allocated table of function pointers.
const TfLiteDelegatePlugin* TfLiteXnnpackDelegatePluginCApi();
/** @} */
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_H_
@@ -0,0 +1,119 @@
/* 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.
==============================================================================*/
// Some simple unit tests of the C API Delegate Plugin for the
// XNNPACK Delegate, implemented using the FlatBuffers C API
// to construct the TFLiteSettings FlatBuffer.
#include <gtest/gtest.h>
#include "pthreadpool.h" // from @pthreadpool
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/c/configuration_reader.h"
#include "tensorflow/lite/core/acceleration/configuration/c/xnnpack_plugin.h"
#include "tensorflow/lite/core/acceleration/configuration/c/xnnpack_plugin_c_test_lib.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
namespace tflite {
class XnnpackTest : public testing::Test {
public:
static constexpr int kNumThreadsForTest = 7;
void SetUp() override {
// Construct a FlatBuffer that contains
// TFLiteSettings { XNNPackSettings { num_threads: kNumThreadsForTest } }.
settings_storage_ =
SettingsStorageCreateWithXnnpackThreads(kNumThreadsForTest);
settings_ = SettingsStorageGetSettings(settings_storage_);
}
void TearDown() override {
SettingsStorageDestroy(settings_storage_);
settings_ = nullptr;
}
~XnnpackTest() override = default;
protected:
SettingsStorage *settings_storage_;
const void
*settings_; // settings_ points into storage owned by settings_storage_.
};
constexpr int XnnpackTest::kNumThreadsForTest;
TEST_F(XnnpackTest, CanCreateAndDestroyDelegate) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
EXPECT_NE(delegate, nullptr);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, CanGetDelegateErrno) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int error_number =
TfLiteXnnpackDelegatePluginCApi()->get_delegate_errno(delegate);
EXPECT_EQ(error_number, 0);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, SetsCorrectThreadCount) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
pthreadpool_t threadpool =
static_cast<pthreadpool_t>(TfLiteXNNPackDelegateGetThreadPool(delegate));
int thread_count = pthreadpool_get_threads_count(threadpool);
EXPECT_EQ(thread_count, kNumThreadsForTest);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, UsesDefaultFlagsByDefault) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, UsesSpecifiedFlagsWhenNonzero) {
SettingsStorageDestroy(settings_storage_);
settings_storage_ = SettingsStorageCreateWithXnnpackFlags(
tflite_XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QU8);
settings_ = SettingsStorageGetSettings(settings_storage_);
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QU8);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
// Settings flags to XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS (zero)
// causes flags to be set to their default values, not zero.
// This is potentially confusing behaviour, but we can't distinguish
// the case when flags isn't set from the case when flags is set to zero.
TEST_F(XnnpackTest, UsesDefaultFlagsWhenZero) {
SettingsStorageDestroy(settings_storage_);
settings_storage_ = SettingsStorageCreateWithXnnpackFlags(
tflite_XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS);
settings_ = SettingsStorageGetSettings(settings_storage_);
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
} // namespace tflite
@@ -0,0 +1,101 @@
/* 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.
==============================================================================*/
// Some tests of the XNNPack Plugin using the FlatBuffers C API.
// This source file is C, not C++, to ensure that we're not accidentally
// depending on the FlatBuffers C++ API here.
#include "tensorflow/lite/core/acceleration/configuration/c/xnnpack_plugin_c_test_lib.h"
#include <stdio.h>
#include <stdlib.h>
#include "third_party/flatcc/include/flatcc/flatcc_builder.h"
#include "tensorflow/lite/core/acceleration/configuration/c/configuration_builder.h"
#include "tensorflow/lite/core/acceleration/configuration/c/configuration_reader.h"
struct SettingsStorage {
// The builder object that allocates the storage.
flatcc_builder_t builder;
// The raw buffer.
void* buffer;
size_t size;
// The parsed TFLiteSettings object.
const struct tflite_TFLiteSettings_table* tflite_settings;
};
typedef struct SettingsStorage SettingsStorage;
SettingsStorage* SettingsStorageCreateWithXnnpackThreads(int num_threads) {
struct SettingsStorage* storage =
(struct SettingsStorage*) malloc(sizeof(SettingsStorage));
flatcc_builder_t* builder = &storage->builder;
flatcc_builder_init(builder);
/* Construct a buffer specific to the schema. */
tflite_TFLiteSettings_start_as_root(builder);
tflite_TFLiteSettings_xnnpack_settings_start(builder);
tflite_XNNPackSettings_num_threads_add(builder, num_threads);
tflite_TFLiteSettings_xnnpack_settings_end(builder);
tflite_TFLiteSettings_end_as_root(builder);
/* Retrieve buffer - see also `flatcc_builder_get_direct_buffer`. */
storage->buffer = flatcc_builder_finalize_buffer(builder, &storage->size);
/* 'Parse' the buffer. (This is actually just an offset lookup.) */
storage->tflite_settings = tflite_TFLiteSettings_as_root(storage->buffer);
return storage;
}
SettingsStorage* SettingsStorageCreateWithXnnpackFlags(
tflite_XNNPackFlags_enum_t flags) {
struct SettingsStorage* storage =
(struct SettingsStorage*) malloc(sizeof(SettingsStorage));
flatcc_builder_t* builder = &storage->builder;
flatcc_builder_init(builder);
/* Construct a buffer specific to the schema. */
tflite_TFLiteSettings_start_as_root(builder);
tflite_TFLiteSettings_xnnpack_settings_start(builder);
tflite_XNNPackSettings_flags_add(builder, flags);
tflite_TFLiteSettings_xnnpack_settings_end(builder);
tflite_TFLiteSettings_end_as_root(builder);
/* Retrieve buffer - see also `flatcc_builder_get_direct_buffer`. */
storage->buffer = flatcc_builder_finalize_buffer(builder, &storage->size);
/* 'Parse' the buffer. (This is actually just an offset lookup.) */
storage->tflite_settings = tflite_TFLiteSettings_as_root(storage->buffer);
return storage;
}
tflite_TFLiteSettings_table_t SettingsStorageGetSettings(
const SettingsStorage* storage) {
return storage->tflite_settings;
}
void SettingsStorageDestroy(SettingsStorage* storage) {
free(storage->buffer);
flatcc_builder_clear(&storage->builder);
storage->tflite_settings = NULL;
storage->buffer = NULL;
storage->size = 0;
free(storage);
}
@@ -0,0 +1,64 @@
/* 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.
==============================================================================*/
// Some library routines for constructing TFLiteSettings FlatBuffers,
// implemented using the FlatBuffers C API.
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_C_TEST_LIB_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_C_TEST_LIB_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "tensorflow/lite/core/acceleration/configuration/c/configuration_reader.h"
// Opaque type for building a TfLiteSettings flatbuffer object.
typedef struct SettingsStorage SettingsStorage;
// Constructs a TFLiteSettings FlatBuffer with the following contents:
//
// tflite_settings {
// xnnpack_settings {
// num_threads: <num_threads>
// }
// }
struct SettingsStorage* SettingsStorageCreateWithXnnpackThreads(
int num_threads);
// Constructs a TFLiteSettings FlatBuffer with the following contents:
//
// tflite_settings {
// xnnpack_settings {
// flags: <flags>
// }
// }
struct SettingsStorage* SettingsStorageCreateWithXnnpackFlags(
tflite_XNNPackFlags_enum_t flags);
// Gets the parsed TFLiteSettings FlatBuffer object from the SettingsStorage.
const struct tflite_TFLiteSettings_table* SettingsStorageGetSettings(
const SettingsStorage* storage);
// Deallocates the settings storage allocated by
// SettingsStorageCreateWithXnnpackThreads or
// SettingsStorageCreateWithXnnpackFlags.
void SettingsStorageDestroy(SettingsStorage* storage);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_C_XNNPACK_PLUGIN_C_TEST_LIB_H_
@@ -0,0 +1,139 @@
/* 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.
==============================================================================*/
// Some very simple unit tests of the C API Delegate Plugin for the
// XNNPACK Delegate.
#include "tensorflow/lite/core/acceleration/configuration/c/xnnpack_plugin.h"
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "pthreadpool.h" // from @pthreadpool
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
namespace tflite {
class XnnpackTest : public testing::Test {
public:
static constexpr int kNumThreadsForTest = 7;
void SetUp() override {
// Construct a FlatBuffer that contains
// TFLiteSettings { XNNPackSettings { num_threads: kNumThreadsForTest } }.
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_num_threads(kNumThreadsForTest);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
}
~XnnpackTest() override = default;
protected:
// settings_ points into storage owned by flatbuffer_builder_.
flatbuffers::FlatBufferBuilder flatbuffer_builder_;
const TFLiteSettings *settings_;
};
constexpr int XnnpackTest::kNumThreadsForTest;
TEST_F(XnnpackTest, CanCreateAndDestroyDelegate) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
EXPECT_NE(delegate, nullptr);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, CanGetDelegateErrno) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int error_number =
TfLiteXnnpackDelegatePluginCApi()->get_delegate_errno(delegate);
EXPECT_EQ(error_number, 0);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, SetsCorrectThreadCount) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
pthreadpool_t threadpool =
static_cast<pthreadpool_t>(TfLiteXNNPackDelegateGetThreadPool(delegate));
int thread_count = pthreadpool_get_threads_count(threadpool);
EXPECT_EQ(thread_count, kNumThreadsForTest);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, UsesDefaultFlagsByDefault) {
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
TEST_F(XnnpackTest, UsesSpecifiedFlagsWhenNonzero) {
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_flags(
tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QU8);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QU8);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
// Settings flags to XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS (zero)
// causes flags to be set to their default values, not zero.
// This is potentially confusing behaviour, but we can't distinguish
// the case when flags isn't set from the case when flags is set to zero.
TEST_F(XnnpackTest, UsesDefaultFlagsWhenZero) {
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_flags(
tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
TfLiteDelegate *delegate =
TfLiteXnnpackDelegatePluginCApi()->create(settings_);
int flags = TfLiteXNNPackDelegateGetFlags(delegate);
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
TfLiteXnnpackDelegatePluginCApi()->destroy(delegate);
}
} // namespace tflite
@@ -0,0 +1,61 @@
/* 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/core/acceleration/configuration/delegate_registry.h"
#include <functional>
#include <memory>
#include <string>
#include "absl/synchronization/mutex.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
namespace tflite {
namespace delegates {
void DelegatePluginRegistry::RegisterImpl(
const std::string& name,
std::function<
std::unique_ptr<DelegatePluginInterface>(const TFLiteSettings&)>
creator_function) {
absl::MutexLock lock(mutex_);
factories_[name] = creator_function;
}
std::unique_ptr<DelegatePluginInterface> DelegatePluginRegistry::CreateImpl(
const std::string& name, const TFLiteSettings& settings) {
absl::MutexLock lock(mutex_);
auto it = factories_.find(name);
return (it != factories_.end()) ? it->second(settings) : nullptr;
}
DelegatePluginRegistry* DelegatePluginRegistry::GetSingleton() {
static auto* instance = new DelegatePluginRegistry();
return instance;
}
std::unique_ptr<DelegatePluginInterface> DelegatePluginRegistry::CreateByName(
const std::string& name, const TFLiteSettings& settings) {
auto* const instance = DelegatePluginRegistry::GetSingleton();
return instance->CreateImpl(name, settings);
}
DelegatePluginRegistry::Register::Register(const std::string& name,
CreatorFunction creator_function) {
auto* const instance = DelegatePluginRegistry::GetSingleton();
instance->RegisterImpl(name, creator_function);
}
} // namespace delegates
} // namespace tflite
@@ -0,0 +1,106 @@
/* 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.
==============================================================================*/
// NOLINTBEGIN(whitespace/line_length)
/// WARNING: Users of TensorFlow Lite should not include this file directly,
/// but should instead include
/// "third_party/tensorflow/lite/acceleration/configuration/delegate_registry.h".
/// Only the TensorFlow Lite implementation itself should include this
/// file directly.
// NOLINTEND(whitespace/line_length)
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_DELEGATE_REGISTRY_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_DELEGATE_REGISTRY_H_
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/c/common.h"
// Defines an interface for TFLite delegate plugins.
//
// The acceleration library aims to support all TFLite delegates based on
// configuration expressed as data (flatbuffers). However, consumers tend to
// care about size and also use a subset of delegates. Hence we don't want to
// statically build against all delegates.
//
// This interface allows plugins to handle specific delegates.
//
// Goal of this interface is not to abstract away all the differences between
// delegates. The goal is only to avoid static linking.
//
// Note to implementers: this interface may change if new delegates don't fit
// into the same design.
namespace tflite {
namespace delegates {
// Same w/ Interpreter::TfLiteDelegatePtr to avoid pulling
// tensorflow/lite/interpreter.h dependency
using TfLiteDelegatePtr =
std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>;
class DelegatePluginInterface {
public:
virtual TfLiteDelegatePtr Create() = 0;
virtual int GetDelegateErrno(TfLiteDelegate* from_delegate) = 0;
virtual ~DelegatePluginInterface() = default;
};
// A stripped-down registry that allows delegate plugins to be created by name.
//
// Limitations:
// - Doesn't allow deregistration.
// - Doesn't check for duplication registration.
//
class DelegatePluginRegistry {
public:
typedef std::function<std::unique_ptr<DelegatePluginInterface>(
const TFLiteSettings&)>
CreatorFunction;
// Returns a DelegatePluginInterface registered with `name` or nullptr if no
// matching plugin found.
// TFLiteSettings is per-plugin, so that the corresponding delegate options
// data lifetime is maintained.
static std::unique_ptr<DelegatePluginInterface> CreateByName(
const std::string& name, const TFLiteSettings& settings);
// Struct to be statically allocated for registration.
struct Register {
Register(const std::string& name, CreatorFunction creator_function);
};
private:
void RegisterImpl(const std::string& name, CreatorFunction creator_function);
std::unique_ptr<DelegatePluginInterface> CreateImpl(
const std::string& name, const TFLiteSettings& settings);
static DelegatePluginRegistry* GetSingleton();
absl::Mutex mutex_;
std::unordered_map<std::string, CreatorFunction> factories_
ABSL_GUARDED_BY(mutex_);
};
} // namespace delegates
} // namespace tflite
#define TFLITE_REGISTER_DELEGATE_FACTORY_FUNCTION_VNAME(name, f) \
static auto* g_delegate_plugin_##name##_ = \
new tflite::delegates::DelegatePluginRegistry::Register(#name, f);
#define TFLITE_REGISTER_DELEGATE_FACTORY_FUNCTION(name, f) \
TFLITE_REGISTER_DELEGATE_FACTORY_FUNCTION_VNAME(name, f);
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_DELEGATE_REGISTRY_H_
@@ -0,0 +1,28 @@
/* 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.
==============================================================================*/
// This file implements the TFLite Delegate Plugin for the NNAPI Delegate.
#include "tensorflow/lite/core/acceleration/configuration/nnapi_plugin.h"
#include "tensorflow/lite/core/acceleration/configuration/delegate_registry.h"
namespace tflite {
namespace delegates {
TFLITE_REGISTER_DELEGATE_FACTORY_FUNCTION(NnapiPlugin, NnapiPlugin::New);
} // namespace delegates
} // namespace tflite
@@ -0,0 +1,174 @@
/* 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.
==============================================================================*/
// NOLINTBEGIN(whitespace/line_length)
/// WARNING: Users of TensorFlow Lite should not include this file directly,
/// but should instead include
/// "third_party/tensorflow/lite/acceleration/configuration/delegate_registry.h".
/// Only the TensorFlow Lite implementation itself should include this
/// file directly.
// NOLINTEND(whitespace/line_length)
#ifndef TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_NNAPI_PLUGIN_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_NNAPI_PLUGIN_H_
// This file provides the NNApiPlugin class, which implements the
// TFLite Delegate Plugin for the NNAPI Delegate.
#include <cstdint>
#include <memory>
#include <string>
#include "absl/memory/memory.h"
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/c/delegate_plugin.h"
#include "tensorflow/lite/core/acceleration/configuration/delegate_registry.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/nnapi/NeuralNetworksTypes.h"
#include "tensorflow/lite/nnapi/nnapi_implementation.h"
namespace tflite {
namespace delegates {
class NnapiPlugin : public DelegatePluginInterface {
public:
TfLiteDelegatePtr Create() override {
std::unique_ptr<tflite::StatefulNnApiDelegate> nnapi_delegate = nullptr;
if (!support_library_handle_) {
nnapi_delegate =
std::make_unique<tflite::StatefulNnApiDelegate>(options_);
} else {
auto nnapi_support_library_driver =
reinterpret_cast<const NnApiSLDriverImplFL5*>(
support_library_handle_);
nnapi_delegate = std::make_unique<tflite::StatefulNnApiDelegate>(
nnapi_support_library_driver, options_);
}
return TfLiteDelegatePtr(
nnapi_delegate.release(), [](TfLiteDelegate* delegate) {
delete static_cast<tflite::StatefulNnApiDelegate*>(delegate);
});
}
int GetDelegateErrno(TfLiteDelegate* from_delegate) override {
auto nnapi_delegate =
static_cast<tflite::StatefulNnApiDelegate*>(from_delegate);
return nnapi_delegate->GetNnApiErrno();
}
static std::unique_ptr<NnapiPlugin> New(
const TFLiteSettings& tflite_settings) {
return std::make_unique<NnapiPlugin>(tflite_settings);
}
explicit NnapiPlugin(const TFLiteSettings& tflite_settings) {
const NNAPISettings* nnapi_settings = tflite_settings.nnapi_settings();
if (!nnapi_settings) return;
if (nnapi_settings->accelerator_name() &&
nnapi_settings->accelerator_name()->Length() != 0) {
accelerator_ = nnapi_settings->accelerator_name()->str();
options_.accelerator_name = accelerator_.c_str();
}
SetCompilationCacheDir(tflite_settings);
SetModelToken(tflite_settings);
options_.execution_preference =
ConvertExecutionPrefence(nnapi_settings->execution_preference());
options_.disallow_nnapi_cpu =
!nnapi_settings->allow_nnapi_cpu_on_android_10_plus();
options_.execution_priority =
ConvertExecutionPriority(nnapi_settings->execution_priority());
options_.allow_fp16 = nnapi_settings->allow_fp16_precision_for_fp32();
options_.use_burst_computation = nnapi_settings->use_burst_computation();
if (tflite_settings.max_delegated_partitions() >= 0) {
options_.max_number_delegated_partitions =
tflite_settings.max_delegated_partitions();
}
support_library_handle_ = nnapi_settings->support_library_handle();
}
const tflite::StatefulNnApiDelegate::Options& Options() { return options_; }
const int64_t GetSupportLibraryHandle() { return support_library_handle_; }
private:
void SetCompilationCacheDir(const TFLiteSettings& tflite_settings) {
if (tflite_settings.compilation_caching_settings() &&
tflite_settings.compilation_caching_settings()->cache_dir() &&
tflite_settings.compilation_caching_settings()->cache_dir()->Length() !=
0) {
cache_dir_ =
tflite_settings.compilation_caching_settings()->cache_dir()->str();
options_.cache_dir = cache_dir_.c_str();
} else if (tflite_settings.nnapi_settings() &&
tflite_settings.nnapi_settings()->cache_directory() &&
tflite_settings.nnapi_settings()->cache_directory()->Length() !=
0) {
cache_dir_ = tflite_settings.nnapi_settings()->cache_directory()->str();
options_.cache_dir = cache_dir_.c_str();
}
}
void SetModelToken(const TFLiteSettings& tflite_settings) {
if (tflite_settings.compilation_caching_settings() &&
tflite_settings.compilation_caching_settings()->model_token() &&
tflite_settings.compilation_caching_settings()
->model_token()
->Length() != 0) {
model_token_ =
tflite_settings.compilation_caching_settings()->model_token()->str();
options_.model_token = model_token_.c_str();
} else if (tflite_settings.nnapi_settings()->model_token() &&
tflite_settings.nnapi_settings()->model_token()->Length() != 0) {
model_token_ = tflite_settings.nnapi_settings()->model_token()->str();
options_.model_token = model_token_.c_str();
}
}
static inline tflite::StatefulNnApiDelegate::Options::ExecutionPreference
ConvertExecutionPrefence(
NNAPIExecutionPreference from_compatibility_preference) {
using TflitePreference =
tflite::StatefulNnApiDelegate::Options::ExecutionPreference;
switch (from_compatibility_preference) {
case NNAPIExecutionPreference_NNAPI_LOW_POWER:
return TflitePreference::kLowPower;
case NNAPIExecutionPreference_NNAPI_FAST_SINGLE_ANSWER:
return TflitePreference::kFastSingleAnswer;
case NNAPIExecutionPreference_NNAPI_SUSTAINED_SPEED:
return TflitePreference::kSustainedSpeed;
default:
return TflitePreference::kUndefined;
}
}
static inline int ConvertExecutionPriority(
NNAPIExecutionPriority from_compatibility_priority) {
switch (from_compatibility_priority) {
case NNAPIExecutionPriority_NNAPI_PRIORITY_LOW:
return ANEURALNETWORKS_PRIORITY_LOW;
case NNAPIExecutionPriority_NNAPI_PRIORITY_MEDIUM:
return ANEURALNETWORKS_PRIORITY_MEDIUM;
case NNAPIExecutionPriority_NNAPI_PRIORITY_HIGH:
return ANEURALNETWORKS_PRIORITY_HIGH;
default:
return ANEURALNETWORKS_PRIORITY_DEFAULT;
}
}
std::string accelerator_, cache_dir_, model_token_;
tflite::StatefulNnApiDelegate::Options options_;
int64_t support_library_handle_ = 0;
};
} // namespace delegates
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_NNAPI_PLUGIN_H_
@@ -0,0 +1,519 @@
/* 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/core/acceleration/configuration/nnapi_plugin.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/delegate_registry.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_kernel.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate_mock_test.h"
#include "tensorflow/lite/kernels/test_util.h"
#include "tensorflow/lite/nnapi/NeuralNetworksTypes.h"
#include "tensorflow/lite/nnapi/nnapi_implementation.h"
#include "tensorflow/lite/schema/schema_generated.h"
// Tests for checking that the NNAPI Delegate plugin correctly handles all the
// options from the flatbuffer.
//
// Checking done at NNAPI call level, as that is where we have a mockable
// layer.
namespace tflite {
namespace {
using delegate::nnapi::NnApiMock;
class SingleAddOpModel : public tflite::SingleOpModel {
public:
// Takes ownership of the passed-in 'delegate'.
void Build(delegates::TfLiteDelegatePtr&& delegate) {
int input = AddInput({tflite::TensorType_FLOAT32, {1, 2, 2}});
int constant = AddConstInput({tflite::TensorType_FLOAT32, {1, 2, 2}},
{1.0f, 1.0f, 1.0f, 1.0f});
AddOutput({tflite::TensorType_FLOAT32, {}});
SetBuiltinOp(tflite::BuiltinOperator_ADD, tflite::BuiltinOptions_AddOptions,
tflite::CreateAddOptions(builder_).Union());
SetDelegate(std::move(delegate));
// Set 'apply_delegate' to false to manually apply the delegate later and
// check its return status.
BuildInterpreter({GetShape(input), GetShape(constant)},
/*num_threads=*/-1,
/*allow_fp32_relax_to_fp16=*/false,
/*apply_delegate=*/false,
/*allocate_and_delegate=*/true);
}
tflite::Interpreter* Interpreter() const { return interpreter_.get(); }
};
class NNAPIPluginTest : public ::testing::Test {
protected:
NNAPIPluginTest() : delegate_(nullptr, [](TfLiteDelegate*) {}) {}
void SetUp() override {
nnapi_ = const_cast<NnApi*>(NnApiImplementation());
nnapi_mock_ = std::make_unique<NnApiMock>(nnapi_);
nnapi_->ANeuralNetworksModel_getSupportedOperationsForDevices =
[](const ANeuralNetworksModel* model,
const ANeuralNetworksDevice* const* devices, uint32_t numDevices,
bool* supportedOps) -> int {
supportedOps[0] = true;
return 0;
};
}
template <NNAPIExecutionPreference input, int output>
void CheckExecutionPreference() {
// Note - this uses a template since the NNAPI functions are C function
// pointers rather than lambdas so can't capture variables.
nnapi_->ANeuralNetworksCompilation_setPreference =
[](ANeuralNetworksCompilation* compilation, int32_t preference) {
return preference - output;
};
CreateDelegate(CreateNNAPISettings(fbb_, 0, 0, 0, input));
// Since delegation succeeds, the model becomes immutable and hence can't
// reuse it.
SingleAddOpModel model;
model.Build(std::move(delegate_));
EXPECT_EQ(model.ApplyDelegate(), kTfLiteOk)
<< " given input: " << input << " expected output: " << output;
}
template <NNAPIExecutionPriority input, int output>
void CheckExecutionPriority() {
// Note - this uses a template since the NNAPI functions are C function
// pointers rather than lambdas so can't capture variables.
nnapi_->ANeuralNetworksCompilation_setPriority =
[](ANeuralNetworksCompilation* compilation, int32_t priority) {
return priority - output;
};
CreateDelegate(CreateNNAPISettings(fbb_, 0, 0, 0,
NNAPIExecutionPreference_UNDEFINED, 0, 0,
/*allow CPU=*/true, input));
// Since delegation succeeds, the model becomes immutable and hence can't
// reuse it.
SingleAddOpModel model;
model.Build(std::move(delegate_));
EXPECT_EQ(model.ApplyDelegate(), kTfLiteOk)
<< " given input: " << input << " expected output: " << output;
}
void CreateDelegate(flatbuffers::Offset<NNAPISettings> nnapi_settings) {
tflite_settings_ = flatbuffers::GetTemporaryPointer(
fbb_,
CreateTFLiteSettings(fbb_, tflite::Delegate_NNAPI, nnapi_settings));
plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"NnapiPlugin", *tflite_settings_);
delegate_ = plugin_->Create();
}
TfLiteStatus ApplyDelegate() {
model_.Build(std::move(delegate_));
return model_.ApplyDelegate();
}
NnApi* nnapi_;
std::unique_ptr<NnApiMock> nnapi_mock_;
SingleAddOpModel model_;
flatbuffers::FlatBufferBuilder fbb_;
const TFLiteSettings* tflite_settings_ = nullptr;
delegates::TfLiteDelegatePtr delegate_;
std::unique_ptr<delegates::DelegatePluginInterface> plugin_;
};
TEST(CompilationCachingFields, SourcedFromNNAPISettingsFields) {
flatbuffers::FlatBufferBuilder fbb;
auto nnapi_settings_cache_dir = fbb.CreateString("nnapi_settings_cache_dir");
auto nnapi_settings_model_token =
fbb.CreateString("nnapi_settings_model_token");
NNAPISettingsBuilder nnapi_settings_builder(fbb);
nnapi_settings_builder.add_cache_directory(nnapi_settings_cache_dir);
nnapi_settings_builder.add_model_token(nnapi_settings_model_token);
auto nnapi_settings = nnapi_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(fbb);
tflite_settings_builder.add_delegate(tflite::Delegate_NNAPI);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
auto tflite_settings = tflite_settings_builder.Finish();
fbb.Finish(tflite_settings);
auto tflite_settings_root =
flatbuffers::GetRoot<TFLiteSettings>(fbb.GetBufferPointer());
::tflite::delegates::NnapiPlugin plugin(*tflite_settings_root);
auto options = plugin.Options();
EXPECT_STREQ(options.cache_dir, "nnapi_settings_cache_dir");
EXPECT_STREQ(options.model_token, "nnapi_settings_model_token");
}
TEST(CompilationCachingFields,
TFLiteSettingsFieldsOverrideNNAPISettingsFields) {
flatbuffers::FlatBufferBuilder fbb;
auto top_level_cache_dir = fbb.CreateString("top_level_cache_dir");
auto top_level_model_token = fbb.CreateString("top_level_model_token");
CompilationCachingSettingsBuilder compilation_caching_settings_builder(fbb);
compilation_caching_settings_builder.add_model_token(top_level_model_token);
compilation_caching_settings_builder.add_cache_dir(top_level_cache_dir);
auto compilation_caching_settings =
compilation_caching_settings_builder.Finish();
auto nnapi_settings_cache_dir = fbb.CreateString("nnapi_settings_cache_dir");
auto nnapi_settings_model_token =
fbb.CreateString("nnapi_settings_model_token");
NNAPISettingsBuilder nnapi_settings_builder(fbb);
nnapi_settings_builder.add_cache_directory(nnapi_settings_cache_dir);
nnapi_settings_builder.add_model_token(nnapi_settings_model_token);
auto nnapi_settings = nnapi_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(fbb);
tflite_settings_builder.add_delegate(tflite::Delegate_NNAPI);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
tflite_settings_builder.add_compilation_caching_settings(
compilation_caching_settings);
auto tflite_settings = tflite_settings_builder.Finish();
fbb.Finish(tflite_settings);
auto tflite_settings_root =
flatbuffers::GetRoot<TFLiteSettings>(fbb.GetBufferPointer());
::tflite::delegates::NnapiPlugin plugin(*tflite_settings_root);
auto options = plugin.Options();
EXPECT_STREQ(options.cache_dir, "top_level_cache_dir");
EXPECT_STREQ(options.model_token, "top_level_model_token");
}
TEST(CompilationCachingFields,
NNAPISettingsFieldsUsedIfTFLiteSettingsFieldsArePresentButEmpty) {
flatbuffers::FlatBufferBuilder fbb;
auto empty_top_level_cache_dir = fbb.CreateString("");
auto empty_top_level_model_token = fbb.CreateString("");
CompilationCachingSettingsBuilder compilation_caching_settings_builder(fbb);
compilation_caching_settings_builder.add_model_token(
empty_top_level_model_token);
compilation_caching_settings_builder.add_cache_dir(empty_top_level_cache_dir);
auto compilation_caching_settings =
compilation_caching_settings_builder.Finish();
auto nnapi_settings_cache_dir = fbb.CreateString("nnapi_settings_cache_dir");
auto nnapi_settings_model_token =
fbb.CreateString("nnapi_settings_model_token");
NNAPISettingsBuilder nnapi_settings_builder(fbb);
nnapi_settings_builder.add_cache_directory(nnapi_settings_cache_dir);
nnapi_settings_builder.add_model_token(nnapi_settings_model_token);
auto nnapi_settings = nnapi_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(fbb);
tflite_settings_builder.add_delegate(tflite::Delegate_NNAPI);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
tflite_settings_builder.add_compilation_caching_settings(
compilation_caching_settings);
auto tflite_settings = tflite_settings_builder.Finish();
fbb.Finish(tflite_settings);
auto tflite_settings_root =
flatbuffers::GetRoot<TFLiteSettings>(fbb.GetBufferPointer());
::tflite::delegates::NnapiPlugin plugin(*tflite_settings_root);
auto options = plugin.Options();
EXPECT_STREQ(options.cache_dir, "nnapi_settings_cache_dir");
EXPECT_STREQ(options.model_token, "nnapi_settings_model_token");
}
TEST(CompilationCachingFields,
FallbackToNNAPISettingsCacheDirFieldIfTFLiteSettingsCacheDirIsMissing) {
flatbuffers::FlatBufferBuilder fbb;
auto top_level_model_token = fbb.CreateString("top_level_model_token");
CompilationCachingSettingsBuilder compilation_caching_settings_builder(fbb);
compilation_caching_settings_builder.add_model_token(top_level_model_token);
auto compilation_caching_settings =
compilation_caching_settings_builder.Finish();
auto nnapi_settings_cache_dir = fbb.CreateString("nnapi_settings_cache_dir");
auto nnapi_settings_model_token =
fbb.CreateString("nnapi_settings_model_token");
NNAPISettingsBuilder nnapi_settings_builder(fbb);
nnapi_settings_builder.add_cache_directory(nnapi_settings_cache_dir);
nnapi_settings_builder.add_model_token(nnapi_settings_model_token);
auto nnapi_settings = nnapi_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(fbb);
tflite_settings_builder.add_delegate(tflite::Delegate_NNAPI);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
tflite_settings_builder.add_compilation_caching_settings(
compilation_caching_settings);
auto tflite_settings = tflite_settings_builder.Finish();
fbb.Finish(tflite_settings);
auto tflite_settings_root =
flatbuffers::GetRoot<TFLiteSettings>(fbb.GetBufferPointer());
::tflite::delegates::NnapiPlugin plugin(*tflite_settings_root);
auto options = plugin.Options();
EXPECT_STREQ(options.cache_dir, "nnapi_settings_cache_dir");
EXPECT_STREQ(options.model_token, "top_level_model_token");
}
TEST(
CompilationCachingFields,
FallbackToNNAPISettingsModelTokenFieldIfTFLiteSettingsModelTokenIsMissing) {
flatbuffers::FlatBufferBuilder fbb;
auto top_level_cache_dir = fbb.CreateString("top_level_cache_dir");
CompilationCachingSettingsBuilder compilation_caching_settings_builder(fbb);
compilation_caching_settings_builder.add_cache_dir(top_level_cache_dir);
auto compilation_caching_settings =
compilation_caching_settings_builder.Finish();
auto nnapi_settings_cache_dir = fbb.CreateString("nnapi_settings_cache_dir");
auto nnapi_settings_model_token =
fbb.CreateString("nnapi_settings_model_token");
NNAPISettingsBuilder nnapi_settings_builder(fbb);
nnapi_settings_builder.add_cache_directory(nnapi_settings_cache_dir);
nnapi_settings_builder.add_model_token(nnapi_settings_model_token);
auto nnapi_settings = nnapi_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(fbb);
tflite_settings_builder.add_delegate(tflite::Delegate_NNAPI);
tflite_settings_builder.add_nnapi_settings(nnapi_settings);
tflite_settings_builder.add_compilation_caching_settings(
compilation_caching_settings);
auto tflite_settings = tflite_settings_builder.Finish();
fbb.Finish(tflite_settings);
auto tflite_settings_root =
flatbuffers::GetRoot<TFLiteSettings>(fbb.GetBufferPointer());
::tflite::delegates::NnapiPlugin plugin(*tflite_settings_root);
auto options = plugin.Options();
EXPECT_STREQ(options.cache_dir, "top_level_cache_dir");
EXPECT_STREQ(options.model_token, "nnapi_settings_model_token");
}
TEST_F(NNAPIPluginTest, PassesAcceleratorNameFailure) {
// Fails with non-existent "foo".
CreateDelegate(CreateNNAPISettings(fbb_, fbb_.CreateString("foo")));
EXPECT_EQ(kTfLiteDelegateError, ApplyDelegate());
}
TEST_F(NNAPIPluginTest, PassesAcceleratorNameSuccess) {
// Succeeds with "test-device" supported by the mock.
CreateDelegate(CreateNNAPISettings(fbb_, fbb_.CreateString("test-device")));
EXPECT_EQ(kTfLiteOk, ApplyDelegate());
}
TEST_F(NNAPIPluginTest, PassesExecutionPreference) {
CheckExecutionPreference<NNAPIExecutionPreference_UNDEFINED,
StatefulNnApiDelegate::Options::kUndefined>();
CheckExecutionPreference<NNAPIExecutionPreference_NNAPI_LOW_POWER,
StatefulNnApiDelegate::Options::kLowPower>();
CheckExecutionPreference<NNAPIExecutionPreference_NNAPI_FAST_SINGLE_ANSWER,
StatefulNnApiDelegate::Options::kFastSingleAnswer>();
CheckExecutionPreference<NNAPIExecutionPreference_NNAPI_SUSTAINED_SPEED,
StatefulNnApiDelegate::Options::kSustainedSpeed>();
}
TEST_F(NNAPIPluginTest, PassesExecutionPriority) {
nnapi_->android_sdk_version =
tflite::delegate::nnapi::kMinSdkVersionForNNAPI13;
CheckExecutionPriority<NNAPIExecutionPriority_NNAPI_PRIORITY_UNDEFINED,
ANEURALNETWORKS_PRIORITY_DEFAULT>();
CheckExecutionPriority<NNAPIExecutionPriority_NNAPI_PRIORITY_LOW,
ANEURALNETWORKS_PRIORITY_LOW>();
CheckExecutionPriority<NNAPIExecutionPriority_NNAPI_PRIORITY_MEDIUM,
ANEURALNETWORKS_PRIORITY_MEDIUM>();
CheckExecutionPriority<NNAPIExecutionPriority_NNAPI_PRIORITY_HIGH,
ANEURALNETWORKS_PRIORITY_HIGH>();
}
TEST_F(NNAPIPluginTest, PassesCachingParameters) {
nnapi_->ANeuralNetworksCompilation_setCaching =
[](ANeuralNetworksCompilation* compilation, const char* cacheDir,
const uint8_t* token) -> int {
if (std::string(cacheDir) != "d") return 1;
// Token is hashed with other bits, just check that it's not empty.
if (std::string(reinterpret_cast<const char*>(token)).empty()) return 2;
return 0;
};
CreateDelegate(CreateNNAPISettings(fbb_, 0, fbb_.CreateString("d"),
fbb_.CreateString("t")));
EXPECT_EQ(kTfLiteOk, ApplyDelegate());
}
TEST_F(NNAPIPluginTest, PassesFalseNNAPICpuFlag) {
CreateDelegate(CreateNNAPISettings(fbb_, 0, 0, 0,
NNAPIExecutionPreference_UNDEFINED, 0, 0,
/* allow CPU */ false));
nnapi_->ANeuralNetworksModel_getSupportedOperationsForDevices =
[](const ANeuralNetworksModel* model,
const ANeuralNetworksDevice* const* devices, uint32_t numDevices,
bool* supportedOps) -> int {
supportedOps[0] = true;
// Since no CPU, should only pass one device.
return numDevices - 1;
};
EXPECT_EQ(kTfLiteOk, ApplyDelegate());
}
TEST_F(NNAPIPluginTest, PassesTrueNNAPICpuFlag) {
CreateDelegate(CreateNNAPISettings(fbb_, 0, 0, 0,
NNAPIExecutionPreference_UNDEFINED, 0, 0,
/* allow CPU */ true));
nnapi_->ANeuralNetworksModel_getSupportedOperationsForDevices =
[](const ANeuralNetworksModel* model,
const ANeuralNetworksDevice* const* devices, uint32_t numDevices,
bool* supportedOps) -> int {
supportedOps[0] = true;
// With CPU allowed, should pass two devices.
return numDevices - 2;
};
EXPECT_EQ(kTfLiteOk, ApplyDelegate());
}
/*
* Building a model with three operations that can be used to create multiple
* delegated partitions.
*
* input1 ---
* | - ADD -- ROUND --
* | | - ADD -- output1
* input2 --- |
* |
* input3 -----------------------
*/
class MultiplePartitionsModel : public tflite::MultiOpModel {
public:
// Takes ownership of the passed-in 'delegate'.
void Build(delegates::TfLiteDelegatePtr&& delegate) {
const tflite::TensorData tensors_data = {tflite::TensorType_FLOAT32,
{1, 2, 2}};
int input1 = AddInput(tensors_data);
int input2 = AddInput(tensors_data);
int input3 = AddInput(tensors_data);
int add_out = AddInnerTensor<float>(tensors_data);
int round_out = AddInnerTensor<float>(tensors_data);
int output = AddOutput(tensors_data);
AddBuiltinOp(
tflite::BuiltinOperator_ADD, tflite::BuiltinOptions_AddOptions,
CreateAddOptions(builder_, ActivationFunctionType_NONE).Union(),
{input1, input2}, {add_out});
AddBuiltinOp(tflite::BuiltinOperator_ROUND, tflite::BuiltinOptions_NONE,
/*builtin_options=*/0, {add_out}, {round_out});
AddBuiltinOp(
tflite::BuiltinOperator_ADD, tflite::BuiltinOptions_AddOptions,
CreateAddOptions(builder_, ActivationFunctionType_NONE).Union(),
{round_out, input3}, {output});
SetDelegate(std::move(delegate));
// Set 'apply_delegate' to false to manually apply the delegate later and
// check its return status.
BuildInterpreter({GetShape(input1), GetShape(input2), GetShape(input3)},
/*num_threads=*/-1,
/*allow_fp32_relax_to_fp16=*/false,
/*apply_delegate=*/false,
/*allocate_and_delegate=*/true);
}
tflite::Interpreter* Interpreter() const { return interpreter_.get(); }
};
class NNAPIMultiOpPluginTest : public ::testing::Test {
protected:
NNAPIMultiOpPluginTest() : delegate_(nullptr, [](TfLiteDelegate*) {}) {}
void SetUp() override {
nnapi_ = const_cast<NnApi*>(NnApiImplementation());
nnapi_mock_ = std::make_unique<NnApiMock>(nnapi_);
}
void CreateDelegate(flatbuffers::Offset<NNAPISettings> nnapi_settings,
int max_delegated_partitions) {
tflite_settings_ = flatbuffers::GetTemporaryPointer(
fbb_,
CreateTFLiteSettings(fbb_, tflite::Delegate_NNAPI, nnapi_settings,
/* gpu_settings */ 0,
/* hexagon_settings */ 0,
/* xnnpack_settings */ 0,
/* coreml_settings */ 0,
/* cpu_settings */ 0, max_delegated_partitions,
/* disable_default_delegates */ false,
/* stable_delegate_loader_settings */ 0));
plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"NnapiPlugin", *tflite_settings_);
delegate_ = plugin_->Create();
}
int CountNnApiPartitions() {
return std::count_if(std::begin(model_.Interpreter()->execution_plan()),
std::end(model_.Interpreter()->execution_plan()),
[this](const int node_index) {
return model_.Interpreter()
->node_and_registration(node_index)
->first.delegate != nullptr;
});
}
TfLiteStatus ApplyDelegate() {
model_.Build(std::move(delegate_));
return model_.ApplyDelegate();
}
NnApi* nnapi_;
std::unique_ptr<NnApiMock> nnapi_mock_;
MultiplePartitionsModel model_;
flatbuffers::FlatBufferBuilder fbb_;
const TFLiteSettings* tflite_settings_ = nullptr;
delegates::TfLiteDelegatePtr delegate_;
std::unique_ptr<delegates::DelegatePluginInterface> plugin_;
};
TEST_F(NNAPIMultiOpPluginTest, PassesMaxDelegatedPartitionsFlag) {
CreateDelegate(CreateNNAPISettings(
fbb_, 0, 0, 0, NNAPIExecutionPreference_UNDEFINED, 0, 0,
/* allow CPU */ true,
/* execution_priority */
tflite::NNAPIExecutionPriority_NNAPI_PRIORITY_UNDEFINED,
/* allow_dynamic_dimensions */ false,
/* allow_fp16_precision_for_fp32 */ false),
/* max_delegated_partitions */ 1);
nnapi_->ANeuralNetworksModel_getSupportedOperationsForDevices =
[](const ANeuralNetworksModel* model,
const ANeuralNetworksDevice* const* devices, uint32_t numDevices,
bool* supportedOps) -> int {
supportedOps[0] = true;
supportedOps[1] = false;
supportedOps[2] = true;
return 0;
};
EXPECT_EQ(kTfLiteOk, ApplyDelegate());
EXPECT_EQ(CountNnApiPartitions(), 1);
}
} // namespace
} // namespace tflite
@@ -0,0 +1,59 @@
/* 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/core/acceleration/configuration/stable_delegate_registry.h"
#include <string>
#include "absl/synchronization/mutex.h"
#include "tensorflow/lite/core/acceleration/configuration/c/stable_delegate.h"
namespace tflite {
namespace delegates {
void StableDelegateRegistry::RegisterStableDelegate(
const TfLiteStableDelegate* delegate) {
auto* const instance = StableDelegateRegistry::GetSingleton();
instance->RegisterStableDelegateImpl(delegate);
}
const TfLiteStableDelegate* StableDelegateRegistry::RetrieveStableDelegate(
const std::string& name) {
auto* const instance = StableDelegateRegistry::GetSingleton();
return instance->RetrieveStableDelegateImpl(name);
}
void StableDelegateRegistry::RegisterStableDelegateImpl(
const TfLiteStableDelegate* delegate) {
absl::MutexLock lock(mutex_);
registry_[delegate->delegate_name] = delegate;
}
const TfLiteStableDelegate* StableDelegateRegistry::RetrieveStableDelegateImpl(
const std::string& name) {
absl::MutexLock lock(mutex_);
if (registry_.find(name) == registry_.end()) {
return nullptr;
} else {
return registry_[name];
}
}
StableDelegateRegistry* StableDelegateRegistry::GetSingleton() {
static auto* instance = new StableDelegateRegistry();
return instance;
}
} // namespace delegates
} // namespace tflite
@@ -0,0 +1,58 @@
/* 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_CORE_ACCELERATION_CONFIGURATION_STABLE_DELEGATE_REGISTRY_H_
#define TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_STABLE_DELEGATE_REGISTRY_H_
#include <string>
#include <unordered_map>
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "tensorflow/lite/core/acceleration/configuration/c/stable_delegate.h"
namespace tflite {
namespace delegates {
// A dedicated singleton registry for TfLiteStableDelegate.
// Note that there is also a non-stable delegate registry
// (third_party/tensorflow/lite/core/acceleration/configuration/
// delegate_registry.h)
// but it does not serve very well for TfLiteStableDelegate as it could not
// register all the information of TfLiteStableDelegate and it uses concrete
// types.
class StableDelegateRegistry {
public:
// Registers a TfLiteStableDelegate pointer to the registry.
static void RegisterStableDelegate(const TfLiteStableDelegate* delegate);
// Retrieves the pointer to the corresponding TfLiteStableDelegate from the
// registry given a delegate name. Returns nullptr if no registration found.
static const TfLiteStableDelegate* RetrieveStableDelegate(
const std::string& name);
private:
static StableDelegateRegistry* GetSingleton();
void RegisterStableDelegateImpl(const TfLiteStableDelegate* delegate);
const TfLiteStableDelegate* RetrieveStableDelegateImpl(
const std::string& name);
absl::Mutex mutex_;
std::unordered_map<std::string, const TfLiteStableDelegate*> registry_
ABSL_GUARDED_BY(mutex_);
};
} // namespace delegates
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ACCELERATION_CONFIGURATION_STABLE_DELEGATE_REGISTRY_H_
@@ -0,0 +1,52 @@
/* 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/core/acceleration/configuration/stable_delegate_registry.h"
#include <gtest/gtest.h>
#include "tensorflow/lite/core/acceleration/configuration/c/stable_delegate.h"
namespace {
using tflite::delegates::StableDelegateRegistry;
TfLiteStableDelegate CreateTestStableDelegate() {
TfLiteStableDelegate stable_delegate = {TFL_STABLE_DELEGATE_ABI_VERSION,
"test_delegate", "V1.0.0", nullptr};
return stable_delegate;
}
class StableDelegateRegistryTest : public testing::Test {
public:
void SetUp() override {
stable_delegate_ = CreateTestStableDelegate();
StableDelegateRegistry::RegisterStableDelegate(&stable_delegate_);
}
protected:
TfLiteStableDelegate stable_delegate_;
};
TEST_F(StableDelegateRegistryTest, TestRetrieval) {
EXPECT_EQ(StableDelegateRegistry::RetrieveStableDelegate("test_delegate"),
&stable_delegate_);
}
TEST_F(StableDelegateRegistryTest, NoRegistrationFound) {
EXPECT_EQ(
StableDelegateRegistry::RetrieveStableDelegate("not_valid_delegate"),
nullptr);
}
} // namespace
@@ -0,0 +1,64 @@
/* 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 <memory>
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/core/acceleration/configuration/delegate_registry.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
namespace tflite {
namespace delegates {
class XNNPackPlugin : public DelegatePluginInterface {
public:
TfLiteDelegatePtr Create() override {
return TfLiteDelegatePtr(TfLiteXNNPackDelegateCreate(&options_),
TfLiteXNNPackDelegateDelete);
}
int GetDelegateErrno(TfLiteDelegate* from_delegate) override { return 0; }
static std::unique_ptr<DelegatePluginInterface> New(
const TFLiteSettings& acceleration) {
return std::make_unique<XNNPackPlugin>(acceleration);
}
explicit XNNPackPlugin(const TFLiteSettings& tflite_settings)
: options_(TfLiteXNNPackDelegateOptionsDefault()) {
// LINT.IfChange(tflite_settings_to_xnnpack_delegate_options)
const auto* xnnpack_settings = tflite_settings.xnnpack_settings();
if (xnnpack_settings) {
options_.num_threads = xnnpack_settings->num_threads();
// If xnnpack_settings->flags is zero, then leave options.flags
// unmodified, i.e. use the default flags (not zero).
// If xnnpack_settings->flags is nonzero, then use exactly
// those flags (i.e. discard the default flags).
if (xnnpack_settings->flags()) {
options_.flags = xnnpack_settings->flags();
}
if (xnnpack_settings->weight_cache_file_path()) {
options_.weight_cache_file_path =
xnnpack_settings->weight_cache_file_path()->c_str();
}
}
// LINT.ThenChange(c/xnnpack_plugin.cc:tflite_settings_to_xnnpack_delegate_options)
}
private:
TfLiteXNNPackDelegateOptions options_;
};
TFLITE_REGISTER_DELEGATE_FACTORY_FUNCTION(XNNPackPlugin, XNNPackPlugin::New);
} // namespace delegates
} // namespace tflite
@@ -0,0 +1,173 @@
/* 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.
==============================================================================*/
// Some very simple unit tests of the (C++) XNNPack Delegate Plugin.
#include <memory>
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "pthreadpool.h" // from @pthreadpool
#include "tensorflow/lite/acceleration/configuration/configuration_generated.h"
#include "tensorflow/lite/core/acceleration/configuration/delegate_registry.h"
#include "tensorflow/lite/delegates/xnnpack/xnnpack_delegate.h"
namespace tflite {
class XnnpackPluginTest : public testing::Test {
public:
static constexpr int kNumThreadsForTest = 7;
void SetUp() override {
// Construct a FlatBuffer that contains
// TFLiteSettings {
// delegate: Delegate.XNNPACK,
// XNNPackSettings {
// num_threads: kNumThreadsForTest
// }
// }.
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_num_threads(kNumThreadsForTest);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
tflite_settings_builder.add_delegate(Delegate_XNNPACK);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
tflite_settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
// Create an XNNPack delegate plugin using the settings from the flatbuffer.
delegate_plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"XNNPackPlugin", *tflite_settings_);
ASSERT_NE(delegate_plugin_, nullptr);
}
void TearDown() override { delegate_plugin_.reset(); }
~XnnpackPluginTest() override = default;
protected:
// settings_ points into storage owned by flatbuffer_builder_.
flatbuffers::FlatBufferBuilder flatbuffer_builder_;
const TFLiteSettings *tflite_settings_;
std::unique_ptr<delegates::DelegatePluginInterface> delegate_plugin_;
};
constexpr int XnnpackPluginTest::kNumThreadsForTest;
TEST_F(XnnpackPluginTest, CanCreateAndDestroyDelegate) {
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
EXPECT_NE(delegate, nullptr);
}
TEST_F(XnnpackPluginTest, CanGetDelegateErrno) {
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
int error_number = delegate_plugin_->GetDelegateErrno(delegate.get());
EXPECT_EQ(error_number, 0);
}
TEST_F(XnnpackPluginTest, SetsCorrectThreadCount) {
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
pthreadpool_t threadpool = static_cast<pthreadpool_t>(
TfLiteXNNPackDelegateGetThreadPool(delegate.get()));
int thread_count = pthreadpool_get_threads_count(threadpool);
EXPECT_EQ(thread_count, kNumThreadsForTest);
}
TEST_F(XnnpackPluginTest, UsesDefaultFlagsByDefault) {
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
int flags = TfLiteXNNPackDelegateGetFlags(delegate.get());
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
}
TEST_F(XnnpackPluginTest, UsesSpecifiedFlagsWhenNonzero) {
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_flags(
tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QS8);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
tflite_settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
delegate_plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"XNNPackPlugin", *tflite_settings_);
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
int flags = TfLiteXNNPackDelegateGetFlags(delegate.get());
EXPECT_EQ(flags, tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_FLAG_QS8);
}
// Settings flags to XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS (zero)
// causes flags to be set to their default values, not zero.
// This is potentially confusing behaviour, but we can't distinguish
// the case when flags isn't set from the case when flags is set to zero.
TEST_F(XnnpackPluginTest, UsesDefaultFlagsWhenZero) {
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_flags(
tflite::XNNPackFlags_TFLITE_XNNPACK_DELEGATE_NO_FLAGS);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
tflite_settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
delegate_plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"XNNPackPlugin", *tflite_settings_);
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
int flags = TfLiteXNNPackDelegateGetFlags(delegate.get());
EXPECT_EQ(flags, TfLiteXNNPackDelegateOptionsDefault().flags);
}
TEST_F(XnnpackPluginTest, DoesNotSetWeightCacheFilePathByDefault) {
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
const TfLiteXNNPackDelegateOptions *options =
TfLiteXNNPackDelegateGetOptions(delegate.get());
EXPECT_EQ(options->weight_cache_file_path, nullptr);
}
TEST_F(XnnpackPluginTest, HonoursWeightCacheFilePathSetting) {
const char *const kWeightCachePath = "/tmp/wcfp";
const auto weight_cache_file_path_string =
flatbuffer_builder_.CreateString(kWeightCachePath);
XNNPackSettingsBuilder xnnpack_settings_builder(flatbuffer_builder_);
xnnpack_settings_builder.add_weight_cache_file_path(
weight_cache_file_path_string);
flatbuffers::Offset<XNNPackSettings> xnnpack_settings =
xnnpack_settings_builder.Finish();
TFLiteSettingsBuilder tflite_settings_builder(flatbuffer_builder_);
tflite_settings_builder.add_xnnpack_settings(xnnpack_settings);
flatbuffers::Offset<TFLiteSettings> tflite_settings =
tflite_settings_builder.Finish();
flatbuffer_builder_.Finish(tflite_settings);
tflite_settings_ = flatbuffers::GetRoot<TFLiteSettings>(
flatbuffer_builder_.GetBufferPointer());
delegate_plugin_ = delegates::DelegatePluginRegistry::CreateByName(
"XNNPackPlugin", *tflite_settings_);
delegates::TfLiteDelegatePtr delegate = delegate_plugin_->Create();
const TfLiteXNNPackDelegateOptions *options =
TfLiteXNNPackDelegateGetOptions(delegate.get());
EXPECT_STREQ(options->weight_cache_file_path, kWeightCachePath);
}
} // namespace tflite
+166
View File
@@ -0,0 +1,166 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite:build_def.bzl", "tflite_copts")
load("//tensorflow/lite:special_rules.bzl", "op_resolver_internal_visibility_allowlist")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:private"],
licenses = ["notice"],
)
filegroup(
name = "tflite_internal_cc_3p_api_deps_src",
srcs = [
":error_reporter.h",
":op_resolver.h",
":op_resolver_internal.h",
":verifier.h",
],
visibility = [
"//tensorflow/lite:__pkg__",
],
)
cc_library(
name = "api",
srcs = [
"flatbuffer_conversions.cc",
"tensor_utils.cc",
],
hdrs = [
"error_reporter.h",
"flatbuffer_conversions.h",
"op_resolver.h",
"profiler.h",
"tensor_utils.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = ["//visibility:public"],
deps = [
":error_reporter",
":op_resolver",
"@flatbuffers//:runtime_cc",
"//tensorflow/lite/core/c:common",
# TODO(b/158301698): consider moving internal:compatibility to a more
# central location.
"//tensorflow/lite/kernels/internal:compatibility",
"//tensorflow/lite/schema:schema_fbs",
],
)
# We define separate targets for "op_resolver" and "error_reporter",
# even though those headers are also exported by the "api" target,
# so that targets which only want to depend on these small abstract base
# class modules can express more fine-grained dependencies without
# pulling in tensor_utils and flatbuffer_conversions.
cc_library(
name = "op_resolver",
srcs = ["op_resolver.cc"],
hdrs = ["op_resolver.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//visibility:public",
],
deps = [
":error_reporter",
"//tensorflow/compiler/mlir/lite/schema:schema_utils",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/schema:schema_fbs",
],
)
cc_library(
name = "error_reporter",
hdrs = [
"error_reporter.h",
"//tensorflow/compiler/mlir/lite/core/api:error_reporter.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//visibility:public",
],
deps = [
"//tensorflow/compiler/mlir/lite/core/api:error_reporter",
],
)
cc_library(
name = "verifier",
hdrs = [
"verifier.h",
"//tensorflow/compiler/mlir/lite/core/api:verifier.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = ["//visibility:public"],
deps = [
"//tensorflow/compiler/mlir/lite/core/api:error_reporter",
"//tensorflow/compiler/mlir/lite/core/api:verifier",
],
)
cc_library(
name = "op_resolver_internal",
hdrs = ["op_resolver_internal.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = op_resolver_internal_visibility_allowlist() + [
"//tensorflow/lite:__pkg__",
"//tensorflow/lite/java/src/main/native:__pkg__",
],
deps = [":op_resolver"],
)
cc_test(
name = "op_resolver_test",
size = "small",
srcs = ["op_resolver_test.cc"],
deps = [
":api",
"//tensorflow/compiler/mlir/lite/core/api:error_reporter",
"//tensorflow/compiler/mlir/lite/schema:schema_conversion_utils",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
],
)
cc_test(
name = "op_resolver_internal_test",
size = "small",
srcs = ["op_resolver_internal_test.cc"],
deps = [
":op_resolver",
":op_resolver_internal",
"//tensorflow/lite:framework",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite/core/kernels:builtin_ops",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "flatbuffer_conversions_test",
size = "small",
srcs = ["flatbuffer_conversions_test.cc"],
deps = [
":api",
"//tensorflow/compiler/mlir/lite/core/api:error_reporter",
"//tensorflow/lite:string",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/schema:schema_fbs",
"@com_google_googletest//:gtest_main",
"@flatbuffers//:runtime_cc",
],
)
+20
View File
@@ -0,0 +1,20 @@
/* 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.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
#define TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
#include "tensorflow/compiler/mlir/lite/core/api/error_reporter.h" // IWYU pragma: export
#endif // TENSORFLOW_LITE_CORE_API_ERROR_REPORTER_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,477 @@
/* 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/compiler/mlir/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/c/common.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() {
static_assert(std::is_trivially_destructible<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 ParseDynamicUpdateSlice(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 ParseEmbeddingLookup(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 ParseReverseV2(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 ParseSelect(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);
TfLiteStatus ParseBitwiseXor(const Operator* op, ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseRightShift(const Operator* op, ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloScatter(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloRngBitGenerator(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloGather(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloReduceWindow(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloPad(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloComposite(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloShiftLeft(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
TfLiteStatus ParseStablehloCase(const Operator* op,
ErrorReporter* error_reporter,
BuiltinDataAllocator* allocator,
void** builtin_data);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_FLATBUFFER_CONVERSIONS_H_
@@ -0,0 +1,946 @@
/* 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/core/api/flatbuffer_conversions.h"
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <tuple>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "tensorflow/compiler/mlir/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/c/builtin_op_data.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/string_type.h"
using testing::AllOf;
using testing::Each;
using testing::ElementsAre;
using testing::Eq;
using testing::HasSubstr;
using testing::StrEq;
namespace tflite {
namespace {
class MockErrorReporter : public ErrorReporter {
public:
MockErrorReporter() : buffer_size_(0) {}
int Report(const char* format, va_list args) override {
buffer_size_ += vsnprintf(buffer_ + buffer_size_,
kBufferSize - buffer_size_, format, args);
return buffer_size_;
}
const char* GetBuffer() const { return buffer_; }
int GetBufferSize() const { return buffer_size_; }
bool IsEmpty() const { return !buffer_size_; }
string GetString() const { return string(buffer_, buffer_size_); }
private:
static constexpr int kBufferSize = 256;
char buffer_[kBufferSize];
int buffer_size_;
};
// Used to determine how the op data parsing function creates its working space.
class MockDataAllocator : public BuiltinDataAllocator {
public:
MockDataAllocator() : is_allocated_(false) {}
void* Allocate(size_t size, size_t alignment_hint) override {
EXPECT_FALSE(is_allocated_);
const int max_size = kBufferSize;
EXPECT_LE(size, max_size);
is_allocated_ = true;
return buffer_;
}
void Deallocate(void* data) override { is_allocated_ = false; }
private:
static constexpr int kBufferSize = 1024;
char buffer_[kBufferSize];
bool is_allocated_;
};
} // namespace
class FlatbufferConversionsTest : public ::testing::Test {
public:
const Operator* BuildTestOperator(BuiltinOptions op_type,
flatbuffers::Offset<void> options) {
flatbuffers::Offset<Operator> offset =
CreateOperatorDirect(builder_, 0, nullptr, nullptr, op_type, options,
nullptr, CustomOptionsFormat_FLEXBUFFERS, nullptr);
builder_.Finish(offset);
void* pointer = builder_.GetBufferPointer();
return flatbuffers::GetRoot<Operator>(pointer);
}
const Operator* BuildTestOperator(BuiltinOptions2 op_type,
flatbuffers::Offset<void> options) {
flatbuffers::Offset<Operator> offset = CreateOperatorDirect(
builder_, /*opcode_index=*/0, /*inputs=*/nullptr, /*outputs=*/nullptr,
/*builtin_options_type=*/tflite::BuiltinOptions_NONE,
/*builtin_options=*/0, /*custom_options=*/nullptr,
/*custom_options_format=*/tflite::CustomOptionsFormat_FLEXBUFFERS,
/*mutating_variable_inputs=*/nullptr, /*intermediates=*/nullptr,
/*large_custom_options_offset=*/0, /*large_custom_options_size=*/0,
/*builtin_options_2_type=*/op_type,
/*builtin_options_2=*/options);
builder_.Finish(offset);
void* pointer = builder_.GetBufferPointer();
return flatbuffers::GetRoot<Operator>(pointer);
}
protected:
MockErrorReporter mock_reporter_;
MockDataAllocator mock_allocator_;
flatbuffers::FlatBufferBuilder builder_;
};
TEST_F(FlatbufferConversionsTest, ParseSqueezeAll) {
const Operator* op = BuildTestOperator(
BuiltinOptions_SqueezeOptions, CreateSqueezeOptions(builder_).Union());
void* output_data = nullptr;
EXPECT_EQ(kTfLiteOk, ParseOpData(op, BuiltinOperator_SQUEEZE, &mock_reporter_,
&mock_allocator_, &output_data));
}
TEST_F(FlatbufferConversionsTest, ParseDynamicReshape) {
const Operator* op = BuildTestOperator(
BuiltinOptions_ReshapeOptions, CreateReshapeOptions(builder_).Union());
void* output_data = nullptr;
EXPECT_EQ(kTfLiteOk, ParseOpData(op, BuiltinOperator_RESHAPE, &mock_reporter_,
&mock_allocator_, &output_data));
}
TEST_F(FlatbufferConversionsTest, TestParseOpDataConv) {
const Operator* conv_op =
BuildTestOperator(BuiltinOptions_Conv2DOptions,
CreateConv2DOptions(builder_, Padding_SAME, 1, 2,
ActivationFunctionType_RELU, 3, 4)
.Union());
void* output_data = nullptr;
EXPECT_EQ(kTfLiteOk,
ParseOpData(conv_op, BuiltinOperator_CONV_2D, &mock_reporter_,
&mock_allocator_, &output_data));
EXPECT_NE(nullptr, output_data);
TfLiteConvParams* params = reinterpret_cast<TfLiteConvParams*>(output_data);
EXPECT_EQ(kTfLitePaddingSame, params->padding);
EXPECT_EQ(1, params->stride_width);
EXPECT_EQ(2, params->stride_height);
EXPECT_EQ(kTfLiteActRelu, params->activation);
EXPECT_EQ(3, params->dilation_width_factor);
EXPECT_EQ(4, params->dilation_height_factor);
}
TEST_F(FlatbufferConversionsTest, ParseBadFullyConnected) {
const Operator* conv_op = BuildTestOperator(
BuiltinOptions_FullyConnectedOptions,
CreateFullyConnectedOptions(
builder_, ActivationFunctionType_RELU,
static_cast<FullyConnectedOptionsWeightsFormat>(-1), true)
.Union());
void* output_data = nullptr;
EXPECT_EQ(kTfLiteError,
ParseOpData(conv_op, BuiltinOperator_FULLY_CONNECTED,
&mock_reporter_, &mock_allocator_, &output_data));
}
TEST_F(FlatbufferConversionsTest, TestParseOpDataCustom) {
const Operator* custom_op =
BuildTestOperator(BuiltinOptions_NONE, flatbuffers::Offset<void>());
void* output_data = nullptr;
EXPECT_EQ(kTfLiteOk,
ParseOpData(custom_op, BuiltinOperator_CUSTOM, &mock_reporter_,
&mock_allocator_, &output_data));
EXPECT_EQ(nullptr, output_data);
}
TEST_F(FlatbufferConversionsTest, TestConvertTensorType) {
TfLiteType type;
EXPECT_EQ(kTfLiteOk,
ConvertTensorType(TensorType_FLOAT32, &type, &mock_reporter_));
EXPECT_EQ(kTfLiteFloat32, type);
}
TEST_F(FlatbufferConversionsTest, TestConvertTensorTypeFloat16) {
TfLiteType type;
EXPECT_EQ(kTfLiteOk,
ConvertTensorType(TensorType_FLOAT16, &type, &mock_reporter_));
EXPECT_EQ(kTfLiteFloat16, type);
}
TEST_F(FlatbufferConversionsTest, TestConvertTensorTypeBFloat16) {
TfLiteType type;
EXPECT_EQ(kTfLiteOk,
ConvertTensorType(TensorType_BFLOAT16, &type, &mock_reporter_));
EXPECT_EQ(kTfLiteBFloat16, type);
}
TEST_F(FlatbufferConversionsTest, TestConvertTensorTypeInt4) {
TfLiteType type;
EXPECT_EQ(kTfLiteOk,
ConvertTensorType(TensorType_INT4, &type, &mock_reporter_));
EXPECT_EQ(kTfLiteInt4, type);
}
class StablehloReduceWindowFlatbufferConversionsTest
: public FlatbufferConversionsTest {
public:
static constexpr int kMaxDims =
TFLITE_STABLEHLO_REDUCE_WINDOW_PARAMS_MAX_DIMENSION_COUNT;
static constexpr int64_t kValidValue = 5;
auto ValidAttr() {
return builder_.CreateVector(std::vector<int64_t>(kMaxDims, kValidValue));
}
auto InvalidAttr() {
return builder_.CreateVector(
std::vector<int64_t>(kMaxDims + 1, kValidValue));
}
auto ValidPaddingAttr() {
return builder_.CreateVector(
std::vector<int64_t>(2 * kMaxDims, kValidValue));
}
auto InvalidPaddingAttr() {
return builder_.CreateVector(
std::vector<int64_t>(2 * kMaxDims + 1, kValidValue));
}
auto EmptyAttr() { return builder_.CreateVector<int64_t>({}); }
};
TEST_F(StablehloReduceWindowFlatbufferConversionsTest, Succeeds) {
const Operator* stablehlo_reduce_window_op = BuildTestOperator(
BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/builder_.CreateVector<int64_t>({1, 2}),
/*window_strides=*/builder_.CreateVector<int64_t>({3, 4}),
/*base_dilations=*/builder_.CreateVector<int64_t>({5, 6}),
/*window_dilations=*/builder_.CreateVector<int64_t>({7, 8}),
/*padding=*/builder_.CreateVector<int64_t>({9, 10, 11, 12}),
/*body_subgraph_index=*/13)
.Union());
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_reduce_window_op,
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, 2),
ElementsAre(1, 2));
EXPECT_THAT(std::make_tuple(output_data->window_strides, 2),
ElementsAre(3, 4));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, 2),
ElementsAre(5, 6));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, 2),
ElementsAre(7, 8));
EXPECT_THAT(std::make_tuple(output_data->padding, 4),
ElementsAre(9, 10, 11, 12));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWithNoWindowDimensions) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/0,
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'window_dimensions' attribute is not optional for "
"'stablehlo.reduce_window' and cannot be empty."));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithNoWindowStrides) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/0,
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims), Each(1));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithNoBaseDilations) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/0,
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims), Each(1));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithNoWindowDilations) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/0,
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(1));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest, SucceedsWithNoPadding) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/0,
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims), Each(0));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWithEmptyWindowDimensions) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/EmptyAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'window_dimensions' attribute is not optional for "
"'stablehlo.reduce_window' and cannot be empty."));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithEmptyWindowStrides) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/EmptyAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims), Each(1));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithEmptyBaseDilations) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/EmptyAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims), Each(1));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithEmptyWindowDilations) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/EmptyAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(1));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims),
Each(kValidValue));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithEmptyPadding) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/EmptyAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
EXPECT_THAT(std::make_tuple(output_data->window_dimensions, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_strides, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->base_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->window_dilations, kMaxDims),
Each(kValidValue));
EXPECT_THAT(std::make_tuple(output_data->padding, 2 * kMaxDims), Each(0));
EXPECT_THAT(output_data->body_subgraph_index, Eq(13));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
SucceedsWithParamsAtMaxDims) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(mock_reporter_.GetString(), StrEq(""));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWhenWindowDimensionsHasMoreThanMaxDims) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/InvalidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
AllOf(HasSubstr("Found too many dimensions in the input array of "
"operation 'stablehlo.reduce_window'."),
HasSubstr("Check the 'window_dimensions' attribute.")));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWhenWindowStridesHasWrongDimCount) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/InvalidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
HasSubstr("'window_strides' attribute of 'stablehlo.reduce_window' does "
"not have the expected size"));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWhenBaseDilationsHasWrongDimCount) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/InvalidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
HasSubstr("'base_dilations' attribute of 'stablehlo.reduce_window' does "
"not have the expected size"));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWhenWindowDilationsHasWrongDimCount) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/InvalidAttr(),
/*padding=*/ValidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
HasSubstr(
"'window_dilations' attribute of 'stablehlo.reduce_window' does "
"not have the expected size"));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest,
FailsWhenPaddingHasWrongDimCount) {
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(ParseOpData(
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_,
/*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/InvalidPaddingAttr(),
/*body_subgraph_index=*/13)
.Union()),
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'padding' attribute of 'stablehlo.reduce_window' does "
"not have the expected size"));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest, FailsWithWrongOptions) {
const Operator* stablehlo_reduce_window_op =
BuildTestOperator(BuiltinOptions2_StablehloReduceWindowOptions, 0);
TfLiteStablehloReduceWindowParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_reduce_window_op,
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
HasSubstr(
"Could not get 'stablehlo.reduce_window' operation parameters."));
}
TEST_F(StablehloReduceWindowFlatbufferConversionsTest, DeathTests) {
const Operator* stablehlo_reduce_window_op = BuildTestOperator(
BuiltinOptions2_StablehloReduceWindowOptions,
CreateStablehloReduceWindowOptions(
builder_, /*window_dimensions=*/ValidAttr(),
/*window_strides=*/ValidAttr(),
/*base_dilations=*/ValidAttr(),
/*window_dilations=*/ValidAttr(),
/*padding=*/ValidPaddingAttr(), /*body_subgraph_index=*/13)
.Union());
TfLiteStablehloReduceWindowParams* output_data = nullptr;
#ifdef NDEBUG
GTEST_SKIP();
#endif
EXPECT_DEATH(
ParseOpData(nullptr, BuiltinOperator_STABLEHLO_REDUCE_WINDOW,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_reduce_window_op,
BuiltinOperator_STABLEHLO_REDUCE_WINDOW, nullptr,
&mock_allocator_, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_reduce_window_op,
BuiltinOperator_STABLEHLO_REDUCE_WINDOW,
&mock_reporter_, nullptr, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_reduce_window_op,
BuiltinOperator_STABLEHLO_REDUCE_WINDOW,
&mock_reporter_, &mock_allocator_, nullptr),
"");
}
class StablehloCompositeFlatbufferConversionsTest
: public FlatbufferConversionsTest {};
TEST_F(StablehloCompositeFlatbufferConversionsTest, Succeeds) {
const Operator* stablehlo_composite_op = BuildTestOperator(
BuiltinOptions2_StableHLOCompositeOptions,
CreateStableHLOCompositeOptions(
builder_, builder_.CreateString("odml.rms_norm.impl"),
/*decomposition_subgraph_index=*/3,
builder_.CreateVector<uint8_t>({1, 2, 3, 4}),
/*composite_attributes_format=*/CustomOptionsFormat_FLEXBUFFERS,
/*version=*/7)
.Union());
TfLiteStablehloCompositeParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_composite_op, BuiltinOperator_STABLEHLO_COMPOSITE,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteOk);
ASSERT_NE(output_data, nullptr);
EXPECT_THAT(output_data->name, StrEq("odml.rms_norm.impl"));
EXPECT_THAT(output_data->version, Eq(7));
EXPECT_THAT(output_data->subgraph_index, Eq(3));
EXPECT_THAT(
std::make_tuple(output_data->attributes, output_data->attributes_size),
ElementsAre(1, 2, 3, 4));
}
TEST_F(StablehloCompositeFlatbufferConversionsTest,
FailsWithMissingCompositeAttributes) {
const Operator* stablehlo_composite_op = BuildTestOperator(
BuiltinOptions2_StableHLOCompositeOptions,
CreateStableHLOCompositeOptions(
builder_, builder_.CreateString("odml.rms_norm.impl"),
/*decomposition_subgraph_index=*/3,
/*composite_attributes=*/0,
/*composite_attributes_format=*/CustomOptionsFormat_FLEXBUFFERS,
/*version=*/7)
.Union());
TfLiteStablehloCompositeParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_composite_op, BuiltinOperator_STABLEHLO_COMPOSITE,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'stablehlo.composite' missing required option "
"'composite_attributes'."));
}
TEST_F(StablehloCompositeFlatbufferConversionsTest, FailsWithMissingName) {
const Operator* stablehlo_composite_op = BuildTestOperator(
BuiltinOptions2_StableHLOCompositeOptions,
CreateStableHLOCompositeOptions(
builder_, /*name=*/0,
/*decomposition_subgraph_index=*/3,
builder_.CreateVector<uint8_t>({1, 2, 3, 4}),
/*composite_attributes_format=*/CustomOptionsFormat_FLEXBUFFERS,
/*version=*/7)
.Union());
TfLiteStablehloCompositeParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_composite_op, BuiltinOperator_STABLEHLO_COMPOSITE,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'stablehlo.composite' missing required option "
"'name'."));
}
class StablehloPadFlatbufferConversionsTest : public FlatbufferConversionsTest {
public:
static constexpr int kMaxDims =
TFLITE_STABLEHLO_PAD_PARAMS_MAX_DIMENSION_COUNT;
static constexpr int64_t kValidValue = 5;
};
TEST_F(StablehloPadFlatbufferConversionsTest, Succeeds) {
const Operator* stablehlo_pad_op = BuildTestOperator(
BuiltinOptions2_StablehloPadOptions,
CreateStablehloPadOptions(
builder_,
/*edge_padding_low=*/builder_.CreateVector<int64_t>({1, 0, -1}),
/*edge_padding_high=*/builder_.CreateVector<int64_t>({2, 0, -2}),
/*interior_padding=*/builder_.CreateVector<int64_t>({3, 0, 3}))
.Union());
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteOk);
EXPECT_THAT(std::make_tuple(output_data->edge_padding_low, 3),
ElementsAre(1, 0, -1));
EXPECT_THAT(std::make_tuple(output_data->edge_padding_high, 3),
ElementsAre(2, 0, -2));
EXPECT_THAT(std::make_tuple(output_data->interior_padding, 3),
ElementsAre(3, 0, 3));
}
TEST_F(StablehloPadFlatbufferConversionsTest, FailsWithMissingLowPadding) {
const Operator* stablehlo_pad_op = BuildTestOperator(
BuiltinOptions2_StablehloPadOptions,
CreateStablehloPadOptions(
builder_,
/*edge_padding_low=*/0,
/*edge_padding_high=*/builder_.CreateVector<int64_t>({2, 0, -2}),
/*interior_padding=*/builder_.CreateVector<int64_t>({3, 0, 3}))
.Union());
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
AllOf(
HasSubstr("Input array not provided for operation 'stablehlo.pad'."),
HasSubstr("Check the 'edge_padding_low' attribute.")));
}
TEST_F(StablehloPadFlatbufferConversionsTest, FailsWithMissingHighPadding) {
const Operator* stablehlo_pad_op = BuildTestOperator(
BuiltinOptions2_StablehloPadOptions,
CreateStablehloPadOptions(
builder_,
/*edge_padding_low=*/builder_.CreateVector<int64_t>({1, 0, -1}),
/*edge_padding_high=*/0,
/*interior_padding=*/builder_.CreateVector<int64_t>({3, 0, 3}))
.Union());
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
AllOf(
HasSubstr("Input array not provided for operation 'stablehlo.pad'."),
HasSubstr("Check the 'edge_padding_high' attribute.")));
}
TEST_F(StablehloPadFlatbufferConversionsTest, FailsWithMissingInteriorPadding) {
const Operator* stablehlo_pad_op = BuildTestOperator(
BuiltinOptions2_StablehloPadOptions,
CreateStablehloPadOptions(
builder_,
/*edge_padding_low=*/builder_.CreateVector<int64_t>({1, 0, -1}),
/*edge_padding_high=*/builder_.CreateVector<int64_t>({2, 0, -2}),
/*interior_padding=*/0)
.Union());
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(
mock_reporter_.GetString(),
AllOf(
HasSubstr("Input array not provided for operation 'stablehlo.pad'."),
HasSubstr("Check the 'interior_padding' attribute.")));
}
TEST_F(StablehloPadFlatbufferConversionsTest, FailsInconsistentSizes) {
const Operator* stablehlo_pad_op = BuildTestOperator(
BuiltinOptions2_StablehloPadOptions,
CreateStablehloPadOptions(
builder_,
/*edge_padding_low=*/builder_.CreateVector<int64_t>({1, 0, -1}),
/*edge_padding_high=*/builder_.CreateVector<int64_t>({2, 0, -2}),
/*interior_padding=*/builder_.CreateVector<int64_t>({3, 0, -3, 5}))
.Union());
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("'stablehlo.pad' operation parameter array sizes are "
"not consistent."));
}
TEST_F(StablehloPadFlatbufferConversionsTest, FailsWithWrongOptions) {
const Operator* stablehlo_pad_op = BuildTestOperator(BuiltinOptions_NONE, 0);
TfLiteStablehloPadParams* output_data = nullptr;
EXPECT_EQ(
ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, (void**)&output_data),
kTfLiteError);
EXPECT_THAT(mock_reporter_.GetString(),
HasSubstr("Could not get 'stablehlo.pad' operation parameters."));
}
TEST_F(StablehloPadFlatbufferConversionsTest, DeathTests) {
const Operator* stablehlo_pad_op = BuildTestOperator(BuiltinOptions_NONE, 0);
TfLiteStablehloPadParams* output_data = nullptr;
#ifdef NDEBUG
GTEST_SKIP();
#endif
EXPECT_DEATH(
ParseOpData(nullptr, BuiltinOperator_STABLEHLO_PAD, &mock_reporter_,
&mock_allocator_, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
nullptr, &mock_allocator_, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, nullptr, (void**)&output_data),
"");
EXPECT_DEATH(ParseOpData(stablehlo_pad_op, BuiltinOperator_STABLEHLO_PAD,
&mock_reporter_, &mock_allocator_, nullptr),
"");
}
} // namespace tflite
+68
View File
@@ -0,0 +1,68 @@
/* 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/core/api/op_resolver.h"
#include "tensorflow/compiler/mlir/lite/core/api/error_reporter.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_utils.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
TfLiteStatus GetRegistrationFromOpCode(
const OperatorCode* opcode, const OpResolver& op_resolver,
ErrorReporter* error_reporter, const TfLiteRegistration** registration) {
TfLiteStatus status = kTfLiteOk;
*registration = nullptr;
auto builtin_code = GetBuiltinCode(opcode);
int version = opcode->version();
if (builtin_code > BuiltinOperator_MAX) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Op builtin_code out of range: %d. Are you using old TFLite binary "
"with newer model?",
builtin_code);
status = kTfLiteError;
} else if (builtin_code != BuiltinOperator_CUSTOM) {
*registration = op_resolver.FindOp(builtin_code, version);
if (*registration == nullptr) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Didn't find op for builtin opcode '%s' version '%d'. "
"An older version of this builtin might be supported. "
"Are you using an old TFLite binary with a newer model?\n",
EnumNameBuiltinOperator(builtin_code), version);
status = kTfLiteError;
}
} else if (!opcode->custom_code()) {
TF_LITE_REPORT_ERROR(
error_reporter,
"Operator with CUSTOM builtin_code has no custom_code.\n");
status = kTfLiteError;
} else {
const char* name = opcode->custom_code()->c_str();
*registration = op_resolver.FindOp(name, version);
if (*registration == nullptr) {
// Do not report error for unresolved custom op, we do the final check
// while preparing ops.
status = kTfLiteError;
}
}
return status;
}
} // namespace tflite
+225
View File
@@ -0,0 +1,225 @@
/* 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_CORE_API_OP_RESOLVER_H_
#define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
#include <cstddef>
#include <functional>
#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "tensorflow/compiler/mlir/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
#ifndef DOXYGEN_SKIP
class OpResolverInternal; // For friend declaration below.
class Subgraph; // For friend declaration below.
namespace internal {
class CommonOpaqueConversionUtil; // For friend declaration below.
class OperatorsCache; // Forward decl.
} // namespace internal
#endif
/// Abstract interface that returns TfLiteRegistrations given op codes or custom
/// op names. This is the mechanism that ops being referenced in the flatbuffer
/// model are mapped to executable function pointers (TfLiteRegistrations).
///
/// The lifetime of the TfLiteRegistration object whose address is
/// returned by FindOp must exceed the lifetime of any InterpreterBuilder or
/// Interpreter created with this OpResolver.
/// Likewise the lifetime of the TfLiteOperator object referenced
/// from the TfLiteRegistration object, if any, must exceed the lifetime of
/// any InterpreterBuilder or Interpreter created with this OpResolver.
class OpResolver {
public:
/// Finds the op registration for a builtin operator by enum code.
virtual const TfLiteRegistration* FindOp(tflite::BuiltinOperator op,
int version) const = 0;
/// Finds the op registration of a custom operator by op name.
virtual const TfLiteRegistration* FindOp(const char* op,
int version) const = 0;
// Represents a sequence of delegates.
using TfLiteDelegatePtrVector =
std::vector<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>>;
// Returns optional delegates for resolving and handling ops in the flatbuffer
// model. This may be used in addition to the standard TfLiteRegistration
// lookup for graph resolution.
// WARNING: This API is deprecated, GetDelegateCreators is preferred.
virtual TfLiteDelegatePtrVector GetDelegates(int num_threads) const {
return {};
}
// Represents a function that creates a TfLite delegate instance.
using TfLiteDelegateCreator =
std::function<std::unique_ptr<TfLiteDelegate, void (*)(TfLiteDelegate*)>(
TfLiteContext* /*context*/)>;
// Represents a sequence of delegate creator functions.
using TfLiteDelegateCreators = std::vector<TfLiteDelegateCreator>;
// Returns a vector of delegate creators to create optional delegates for
// resolving and handling ops in the flatbuffer model. This may be used in
// addition to the standard TfLiteRegistration lookup for graph resolution.
//
// Note that this method is not used (will not be called) if you are using
// TF Lite in Google Play Services; the GetOpaqueDelegateCreators method
// (see below) is used for that case.
virtual TfLiteDelegateCreators GetDelegateCreators() const { return {}; }
// TODO(b/202712825): it would be nice if we could avoid the need for separate
// "opaque" types & methods for use only with TF Lite in Google Play Services.
// Represents an opaque delegate instance.
// WARNING: Experimental interface, subject to change.
using TfLiteOpaqueDelegatePtr =
std::unique_ptr<TfLiteOpaqueDelegate, void (*)(TfLiteOpaqueDelegate*)>;
// Represents a function that creates an opaque delegate instance.
// WARNING: Experimental interface, subject to change.
using TfLiteOpaqueDelegateCreator =
std::function<TfLiteOpaqueDelegatePtr(int /*num_threads*/)>;
// Represents a sequence of opaque delegate creator functions.
// WARNING: Experimental interface, subject to change.
using TfLiteOpaqueDelegateCreators = std::vector<TfLiteOpaqueDelegateCreator>;
// Returns a vector of opaque delegate creators to create optional opaque
// delegates for resolving and handling ops in the flatbuffer model. This may
// be used in addition to the standard TfLiteRegistration lookup for graph
// resolution.
//
// Note that this method will be called only if you are using TF Lite in
// Google Play Services; if you are using regular TF Lite, GetDelegateCreators
// (see above) is used instead.
//
// WARNING: Experimental interface, subject to change.
virtual TfLiteOpaqueDelegateCreators GetOpaqueDelegateCreators() const {
return {};
}
virtual ~OpResolver() = default;
OpResolver() = default;
OpResolver(const OpResolver& other) = default;
private:
/// Returns true if this OpResolver may contain any "user defined" ops.
/// By "user defined" ops, we mean any op definitions other than those
/// contained in tflite::ops::builtin::BuiltinOpResolver.
///
/// If this method returns true, it doesn't necessarily mean that the
/// OpResolver contains a user-defined op, just that the absence of
/// user-defined ops can't be guaranteed.
///
/// Note that "user-defined" ops are not the same as "custom" ops;
/// BuiltinOpResolver may support certain "custom" ops, in addition to
/// "builtin" ops, and may not support all of the "builtin" op enum values.
virtual bool MayContainUserDefinedOps() const { return true; }
#ifndef DOXYGEN_SKIP
friend class OpResolverInternal;
friend class Subgraph; // For OpId.
friend class tflite::internal::CommonOpaqueConversionUtil;
friend class tflite::internal::OperatorsCache;
#endif
// This holds the identity of an operator.
// Ths is used as the key for the OperatorsCache below.
struct OpId {
int builtin_code;
const char* custom_name;
int version;
bool operator==(const OpId& other) const {
return builtin_code == other.builtin_code &&
custom_name == other.custom_name && version == other.version;
}
struct Hasher {
size_t operator()(const OpId& op_id) const {
size_t hash_builtin_code = std::hash<int>()(op_id.builtin_code);
size_t hash_custom_name =
op_id.custom_name != nullptr
? std::hash<std::string>()(std::string(op_id.custom_name))
: 0;
size_t hash_version = std::hash<int>()(op_id.version);
return Combine(hash_builtin_code,
Combine(hash_custom_name, hash_version));
}
private:
static size_t Combine(size_t hash1, size_t hash2) {
constexpr int num_bits_to_rotate_left = 21;
constexpr int num_bits_to_rotate_right =
std::numeric_limits<size_t>::digits - num_bits_to_rotate_left;
size_t hash1_rotated = (hash1 << num_bits_to_rotate_left) |
(hash1 >> num_bits_to_rotate_right);
return hash1_rotated + hash2;
}
};
};
// A set of 'TfLiteOperator' objects whose lifetimes need to
// last at least as long as the lifetime of the OpResolver.
// We use shared_ptr rather than unique_ptr here, to allow the
// OperatorsCache to be shared with other classes such as the
// InterpreterBuilder and Interpreter. This is so that the
// TfLiteOperator objects allocated by an OpResolver,
// which may be referenced by a Subgraph in an Interpreter, can remain live
// even if the OpResolver is destroyed, while also allowing the same
// OpResolver to be used with multiple InterpreterBuilders and multiple
// Interpreters.
mutable std::shared_ptr<internal::OperatorsCache>
registration_externals_cache_;
};
#ifndef DOXYGEN_SKIP
// Type for a set of owned 'TfLiteOperator' objects.
// This is needed when converting TfLiteRegistration to
// TfLiteOperator, to ensure that the number of
// TfLiteOperator objects that we allocate is bounded, and to
// ensure that those objects get deallocated at the appropriate time.
// We use a public class rather than a typedef or using declaration here,
// to ensure that the class can be forward-declared.
// WARNING: Experimental interface, subject to change.
namespace internal {
class OperatorsCache
: private std::unordered_map<OpResolver::OpId,
std::unique_ptr<TfLiteOperator>,
OpResolver::OpId::Hasher> {
friend class ::tflite::Subgraph;
friend class ::tflite::internal::CommonOpaqueConversionUtil;
};
} // namespace internal
#endif
// Handles the logic for converting between an OperatorCode structure extracted
// from a flatbuffer and information about a registered operator
// implementation.
TfLiteStatus GetRegistrationFromOpCode(const OperatorCode* opcode,
const OpResolver& op_resolver,
ErrorReporter* error_reporter,
const TfLiteRegistration** registration);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_OP_RESOLVER_H_
@@ -0,0 +1,50 @@
/* 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_OP_RESOLVER_INTERNAL_H_
#define TENSORFLOW_LITE_CORE_API_OP_RESOLVER_INTERNAL_H_
/// \file
///
/// This header op_resolver_internal.h exists so that we can have fine-grained
/// access control on the MayContainUserDefinedOps method
/// and registration_externals_cache_ member.
#include <memory>
#include "tensorflow/lite/core/api/op_resolver.h"
namespace tflite {
class OpResolverInternal {
public:
OpResolverInternal() = delete;
static bool MayContainUserDefinedOps(const OpResolver& op_resolver) {
return op_resolver.MayContainUserDefinedOps();
}
// Get a shared_ptr to the OperatorsCache from an OpResolver.
// This is used to allow the InterpreterBuilder and OpResolver to share
// the same OperatorsCache, so that the Operator objects in it can persist
// for the lifetimes of both the InterpreterBuilder and OpResolver.
static std::shared_ptr<::tflite::internal::OperatorsCache> GetSharedCache(
const ::tflite::OpResolver& op_resolver) {
return op_resolver.registration_externals_cache_;
}
};
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_OP_RESOLVER_INTERNAL_H_
@@ -0,0 +1,120 @@
/* 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/core/api/op_resolver_internal.h"
#include <gtest/gtest.h>
#include "tensorflow/lite/core/api/op_resolver.h"
#include "tensorflow/lite/core/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/core/kernels/register.h"
#include "tensorflow/lite/mutable_op_resolver.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
using ops::builtin::BuiltinOpResolver;
using ops::builtin::BuiltinOpResolverWithoutDefaultDelegates;
namespace {
TEST(OpResolverInternal, ObjectSlicing) {
BuiltinOpResolver op_resolver1;
EXPECT_FALSE(op_resolver1.GetDelegateCreators().empty());
BuiltinOpResolverWithoutDefaultDelegates op_resolver2;
EXPECT_TRUE(op_resolver2.GetDelegateCreators().empty());
// Here, we assign a BuiltinOpResolverWithoutDefaultDelegates instance to a
// BuiltinOpResolver variable where the empty default copy constructor of
// BuiltinOpResolver will be invoked. Even though "object slicing"
// (https://en.wikipedia.org/wiki/Object_slicing) occurs, we still expect an
// empty delegate creator list.
BuiltinOpResolver op_resolver3(op_resolver2);
EXPECT_TRUE(op_resolver3.GetDelegateCreators().empty());
MutableOpResolver op_resolver4(op_resolver1);
EXPECT_FALSE(op_resolver4.GetDelegateCreators().empty());
MutableOpResolver op_resolver5(op_resolver2);
EXPECT_TRUE(op_resolver5.GetDelegateCreators().empty());
}
TEST(OpResolverInternal, BuiltinOpResolverContainsOnlyPredefinedOps) {
BuiltinOpResolver builtin_op_resolver;
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(builtin_op_resolver),
false);
}
TEST(OpResolverInternal, EmptyMutableOpResolverContainsOnlyPredefinedOps) {
MutableOpResolver empty_mutable_op_resolver;
EXPECT_EQ(
OpResolverInternal::MayContainUserDefinedOps(empty_mutable_op_resolver),
false);
}
TEST(OpResolverInternal,
MutableOpResolverAddBuiltinNullptrContainsOnlyPredefinedOps) {
MutableOpResolver mutable_op_resolver;
mutable_op_resolver.AddBuiltin(BuiltinOperator_ADD, nullptr, 1);
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(mutable_op_resolver),
false);
}
TEST(OpResolverInternal,
MutableOpResolverRedefineBuiltinDoesNotContainOnlyPredefinedOps) {
MutableOpResolver mutable_op_resolver;
// Redefine the "add" op with a non-standard meaning ("multiply").
mutable_op_resolver.AddBuiltin(BuiltinOperator_ADD,
tflite::ops::builtin::Register_MUL(), 1);
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(mutable_op_resolver),
true);
}
TEST(OpResolverInternal,
MutableOpResolverAddCustomDoesNotContainOnlyPredefinedOps) {
MutableOpResolver mutable_op_resolver;
mutable_op_resolver.AddCustom("my_custom_op",
tflite::ops::builtin::Register_ADD(), 1);
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(mutable_op_resolver),
true);
}
class ChainableOpResolver : public MutableOpResolver {
public:
using MutableOpResolver::ChainOpResolver;
};
TEST(OpResolverInternal, ChainedBuiltinOpResolverContainOnlyPredefinedOps) {
BuiltinOpResolver builtin_op_resolver;
ChainableOpResolver chainable_op_resolver;
chainable_op_resolver.ChainOpResolver(&builtin_op_resolver);
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(chainable_op_resolver),
false);
}
TEST(OpResolverInternal,
ChainedCustomOpResolverDoesNotContainOnlyPredefinedOps) {
MutableOpResolver mutable_op_resolver;
mutable_op_resolver.AddCustom("my_custom_op",
tflite::ops::builtin::Register_ADD(), 1);
ChainableOpResolver chainable_op_resolver;
chainable_op_resolver.ChainOpResolver(&mutable_op_resolver);
EXPECT_EQ(OpResolverInternal::MayContainUserDefinedOps(chainable_op_resolver),
true);
}
} // anonymous namespace
} // namespace tflite
@@ -0,0 +1,203 @@
/* 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/core/api/op_resolver.h"
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <gtest/gtest.h>
#include "flatbuffers/buffer.h" // from @flatbuffers
#include "flatbuffers/flatbuffer_builder.h" // from @flatbuffers
#include "tensorflow/compiler/mlir/lite/core/api/error_reporter.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_conversion_utils.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/schema/schema_generated.h"
namespace tflite {
namespace {
void* MockInit(TfLiteContext* context, const char* buffer, size_t length) {
// Do nothing.
return nullptr;
}
void MockFree(TfLiteContext* context, void* buffer) {
// Do nothing.
}
TfLiteStatus MockPrepare(TfLiteContext* context, TfLiteNode* node) {
return kTfLiteOk;
}
TfLiteStatus MockInvoke(TfLiteContext* context, TfLiteNode* node) {
return kTfLiteOk;
}
class MockOpResolver : public OpResolver {
public:
const TfLiteRegistration* FindOp(BuiltinOperator op,
int version) const override {
if (op == BuiltinOperator_CONV_2D) {
static TfLiteRegistration r = {MockInit, MockFree, MockPrepare,
MockInvoke};
return &r;
} else {
return nullptr;
}
}
const TfLiteRegistration* FindOp(const char* op, int version) const override {
if (strcmp(op, "mock_custom") == 0) {
static TfLiteRegistration r = {MockInit, MockFree, MockPrepare,
MockInvoke};
return &r;
} else {
return nullptr;
}
}
};
class MockErrorReporter : public ErrorReporter {
public:
MockErrorReporter() : buffer_size_(0) {}
int Report(const char* format, va_list args) override {
buffer_size_ = vsnprintf(buffer_, kBufferSize, format, args);
return buffer_size_;
}
char* GetBuffer() { return buffer_; }
int GetBufferSize() { return buffer_size_; }
private:
static constexpr int kBufferSize = 256;
char buffer_[kBufferSize];
int buffer_size_;
};
} // namespace
TEST(OpResolver, TestResolver) {
MockOpResolver mock_resolver;
OpResolver* resolver = &mock_resolver;
const TfLiteRegistration* registration =
resolver->FindOp(BuiltinOperator_CONV_2D, 0);
EXPECT_NE(nullptr, registration);
EXPECT_EQ(nullptr, registration->init(nullptr, nullptr, 0));
EXPECT_EQ(kTfLiteOk, registration->prepare(nullptr, nullptr));
EXPECT_EQ(kTfLiteOk, registration->invoke(nullptr, nullptr));
registration = resolver->FindOp(BuiltinOperator_CAST, 0);
EXPECT_EQ(nullptr, registration);
registration = resolver->FindOp("mock_custom", 0);
EXPECT_NE(nullptr, registration);
EXPECT_EQ(nullptr, registration->init(nullptr, nullptr, 0));
EXPECT_EQ(kTfLiteOk, registration->prepare(nullptr, nullptr));
EXPECT_EQ(kTfLiteOk, registration->invoke(nullptr, nullptr));
registration = resolver->FindOp("nonexistent_custom", 0);
EXPECT_EQ(nullptr, registration);
}
TEST(OpResolver, TestGetRegistrationFromOpCodeConv) {
MockOpResolver mock_resolver;
OpResolver* resolver = &mock_resolver;
MockErrorReporter mock_reporter;
ErrorReporter* reporter = &mock_reporter;
flatbuffers::FlatBufferBuilder builder;
flatbuffers::Offset<OperatorCode> conv_offset =
CreateOperatorCodeDirect(builder, BuiltinOperator_CONV_2D, nullptr, 0);
builder.Finish(conv_offset);
void* conv_pointer = builder.GetBufferPointer();
const OperatorCode* conv_code =
flatbuffers::GetRoot<OperatorCode>(conv_pointer);
const TfLiteRegistration* registration = nullptr;
EXPECT_EQ(kTfLiteOk, GetRegistrationFromOpCode(conv_code, *resolver, reporter,
&registration));
EXPECT_NE(nullptr, registration);
EXPECT_EQ(nullptr, registration->init(nullptr, nullptr, 0));
EXPECT_EQ(kTfLiteOk, registration->prepare(nullptr, nullptr));
EXPECT_EQ(kTfLiteOk, registration->invoke(nullptr, nullptr));
EXPECT_EQ(0, mock_reporter.GetBufferSize());
}
TEST(OpResolver, TestGetRegistrationFromOpCodeCast) {
MockOpResolver mock_resolver;
OpResolver* resolver = &mock_resolver;
MockErrorReporter mock_reporter;
ErrorReporter* reporter = &mock_reporter;
flatbuffers::FlatBufferBuilder builder;
flatbuffers::Offset<OperatorCode> conv_offset =
CreateOperatorCodeDirect(builder, BuiltinOperator_CAST, nullptr, 0);
builder.Finish(conv_offset);
void* conv_pointer = builder.GetBufferPointer();
const OperatorCode* conv_code =
flatbuffers::GetRoot<OperatorCode>(conv_pointer);
const TfLiteRegistration* registration = nullptr;
EXPECT_EQ(kTfLiteError, GetRegistrationFromOpCode(conv_code, *resolver,
reporter, &registration));
EXPECT_EQ(nullptr, registration);
EXPECT_NE(0, mock_reporter.GetBufferSize());
}
TEST(OpResolver, TestGetRegistrationFromOpCodeCustom) {
MockOpResolver mock_resolver;
OpResolver* resolver = &mock_resolver;
MockErrorReporter mock_reporter;
ErrorReporter* reporter = &mock_reporter;
flatbuffers::FlatBufferBuilder builder;
flatbuffers::Offset<OperatorCode> conv_offset = CreateOperatorCodeDirect(
builder, BuiltinOperator_CUSTOM, "mock_custom", 0);
builder.Finish(conv_offset);
void* conv_pointer = builder.GetBufferPointer();
const OperatorCode* conv_code =
flatbuffers::GetRoot<OperatorCode>(conv_pointer);
const TfLiteRegistration* registration = nullptr;
EXPECT_EQ(kTfLiteOk, GetRegistrationFromOpCode(conv_code, *resolver, reporter,
&registration));
EXPECT_NE(nullptr, registration);
EXPECT_EQ(nullptr, registration->init(nullptr, nullptr, 0));
EXPECT_EQ(kTfLiteOk, registration->prepare(nullptr, nullptr));
EXPECT_EQ(kTfLiteOk, registration->invoke(nullptr, nullptr));
EXPECT_EQ(0, mock_reporter.GetBufferSize());
}
TEST(OpResolver, TestGetRegistrationFromOpCodeNonexistentCustom) {
MockOpResolver mock_resolver;
OpResolver* resolver = &mock_resolver;
MockErrorReporter mock_reporter;
ErrorReporter* reporter = &mock_reporter;
flatbuffers::FlatBufferBuilder builder;
flatbuffers::Offset<OperatorCode> conv_offset = CreateOperatorCodeDirect(
builder, BuiltinOperator_CUSTOM, "nonexistent_custom", 0);
builder.Finish(conv_offset);
void* conv_pointer = builder.GetBufferPointer();
const OperatorCode* conv_code =
flatbuffers::GetRoot<OperatorCode>(conv_pointer);
const TfLiteRegistration* registration = nullptr;
EXPECT_EQ(kTfLiteError, GetRegistrationFromOpCode(conv_code, *resolver,
reporter, &registration));
EXPECT_EQ(nullptr, registration);
// There is no error, since unresolved custom ops are checked while preparing
// nodes.
EXPECT_EQ(0, mock_reporter.GetBufferSize());
}
} // namespace tflite
+250
View File
@@ -0,0 +1,250 @@
/* 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.
==============================================================================*/
#ifndef TENSORFLOW_LITE_CORE_API_PROFILER_H_
#define TENSORFLOW_LITE_CORE_API_PROFILER_H_
#include <cstdint>
namespace tflite {
// A simple utility for enabling profiled event tracing in TensorFlow Lite.
class Profiler {
public:
// As certain Profiler instance might be only interested in certain event
// types, we define each event type value to allow a Profiler to use
// bitmasking bitwise operations to determine whether an event should be
// recorded or not.
enum class EventType {
// Default event type, the metadata field has no special significance.
DEFAULT = 1,
// The event is an operator invocation and the event_metadata field is the
// index of operator node.
OPERATOR_INVOKE_EVENT = 1 << 1,
// The event is an invocation for an internal operator of a TFLite delegate.
// The event_metadata field is the index of operator node that's specific to
// the delegate.
DELEGATE_OPERATOR_INVOKE_EVENT = 1 << 2,
// A delegate op invoke event that profiles a delegate op in the
// Operator-wise Profiling section and not in the Delegate internal section.
DELEGATE_PROFILED_OPERATOR_INVOKE_EVENT = 1 << 3,
// The event is a recording of runtime instrumentation such as the overall
// TFLite runtime status, the TFLite delegate status (if a delegate
// is applied), and the overall model inference latency etc.
// Note, the delegate status and overall status are stored as separate
// event_metadata fields. In particular, the delegate status is encoded
// as DelegateStatus::full_status().
GENERAL_RUNTIME_INSTRUMENTATION_EVENT = 1 << 4,
// Telemetry events. Users and code instrumentations should invoke Telemetry
// calls instead of using the following types directly.
// See experimental/telemetry:profiler for definition of each metadata.
//
// A telemetry event that reports model and interpreter level events.
TELEMETRY_EVENT = 1 << 5,
// A telemetry event that reports model and interpreter level settings.
TELEMETRY_REPORT_SETTINGS = 1 << 6,
// A telemetry event that reports delegate level events.
TELEMETRY_DELEGATE_EVENT = 1 << 7,
// A telemetry event that reports delegate settings.
TELEMETRY_DELEGATE_REPORT_SETTINGS = 1 << 8,
};
virtual ~Profiler() {}
// Signals the beginning of an event and returns a handle to the profile
// event. The `event_metadata1` and `event_metadata2` have different
// interpretations based on the actual Profiler instance and the `event_type`.
// For example, as for the 'SubgraphAwareProfiler' defined in
// lite/core/subgraph.h, when the event_type is OPERATOR_INVOKE_EVENT,
// `event_metadata1` represents the index of a TFLite node, and
// `event_metadata2` represents the index of the subgraph that this event
// comes from.
virtual uint32_t BeginEvent(const char* tag, EventType event_type,
int64_t event_metadata1,
int64_t event_metadata2) = 0;
// Similar w/ the above, but `event_metadata2` defaults to 0.
uint32_t BeginEvent(const char* tag, EventType event_type,
int64_t event_metadata) {
return BeginEvent(tag, event_type, event_metadata, /*event_metadata2*/ 0);
}
// Signals an end to the specified profile event with 'event_metadata's, This
// is useful when 'event_metadata's are not available when the event begins
// or when one wants to overwrite the 'event_metadata's set at the beginning.
virtual void EndEvent(uint32_t event_handle, int64_t event_metadata1,
int64_t event_metadata2) {
// By default discards the metadata.
EndEvent(event_handle);
}
// Signals an end to the specified profile event.
virtual void EndEvent(uint32_t event_handle) = 0;
// Appends an event of type 'event_type' with 'tag' and 'event_metadata'
// which ran for elapsed_time.
// Note:
// In cases were ProfileSummarizer and tensorflow::StatsCalculator are used
// they assume the value is in "usec", if in any case subclasses
// didn't put usec, then the values are not meaningful.
// TODO(karimnosseir): karimnosseir: Revisit and make the function more clear.
void AddEvent(const char* tag, EventType event_type, uint64_t elapsed_time,
int64_t event_metadata) {
AddEvent(tag, event_type, elapsed_time, event_metadata,
/*event_metadata2*/ 0);
}
// Adds a profiler event.
// `metric` field has different intreptation based on `event_type`.
// e.g. it means elapsed time for [DELEGATE_]OPERATOR_INVOKE_EVENT types,
// and interprets as source and status code for TELEMETRY_[DELEGATE_]EVENT
// event types. If the concrete profiler does not provide an implementation,
// does nothing.
// TODO(b/241982974): Clean up dependencies and make it pure virtual.
virtual void AddEvent(const char* tag, EventType event_type, uint64_t metric,
int64_t event_metadata1, int64_t event_metadata2) {}
// Adds a profiler event with data.
// Data will be a const TelemetrySettings* for TELEMETRY_REPORT_SETTINGS
// and TELEMETRY_DELEGATE_REPORT_SETTINGS.
// If the concrete profiler does not provide an implementation, does nothing.
// TODO(b/241982974): Clean up dependencies and make it pure virtual.
virtual void AddEventWithData(const char* tag, EventType event_type,
const void* data) {}
protected:
friend class ScopedProfile;
};
// Adds a profile event to `profiler` that begins with the construction
// of the object and ends when the object goes out of scope.
// The lifetime of tag should be at least the lifetime of `profiler`.
// `profiler` may be null, in which case nothing is profiled.
class ScopedProfile {
public:
ScopedProfile(Profiler* profiler, const char* tag,
Profiler::EventType event_type = Profiler::EventType::DEFAULT,
int64_t event_metadata = 0)
: profiler_(profiler), event_handle_(0) {
if (profiler) {
event_handle_ = profiler_->BeginEvent(tag, event_type, event_metadata);
}
}
~ScopedProfile() {
if (profiler_) {
profiler_->EndEvent(event_handle_);
}
}
protected:
Profiler* profiler_;
uint32_t event_handle_;
};
class ScopedOperatorProfile : public ScopedProfile {
public:
ScopedOperatorProfile(Profiler* profiler, const char* tag, int node_index)
: ScopedProfile(profiler, tag, Profiler::EventType::OPERATOR_INVOKE_EVENT,
static_cast<uint32_t>(node_index)) {}
};
class ScopedDelegateOperatorProfile : public ScopedProfile {
public:
ScopedDelegateOperatorProfile(Profiler* profiler, const char* tag,
int node_index)
: ScopedProfile(profiler, tag,
Profiler::EventType::DELEGATE_OPERATOR_INVOKE_EVENT,
static_cast<uint32_t>(node_index)) {}
};
class ScopedDelegateProfiledOperatorProfile : public ScopedProfile {
public:
ScopedDelegateProfiledOperatorProfile(Profiler* profiler, const char* tag,
int node_index)
: ScopedProfile(
profiler, tag,
Profiler::EventType::DELEGATE_PROFILED_OPERATOR_INVOKE_EVENT,
static_cast<uint32_t>(node_index)) {}
};
// Similar to ScopedProfile but has extra event metadata for EndEvent.
class ScopedRuntimeInstrumentationProfile {
public:
ScopedRuntimeInstrumentationProfile(Profiler* profiler, const char* tag)
: profiler_(profiler), event_handle_(0) {
if (profiler) {
event_handle_ = profiler_->BeginEvent(
tag, Profiler::EventType::GENERAL_RUNTIME_INSTRUMENTATION_EVENT,
/*event_metadata=*/-1);
}
}
void set_runtime_status(int64_t delegate_status, int64_t interpreter_status) {
if (profiler_) {
delegate_status_ = delegate_status;
interpreter_status_ = interpreter_status;
}
}
~ScopedRuntimeInstrumentationProfile() {
if (profiler_) {
profiler_->EndEvent(event_handle_, delegate_status_, interpreter_status_);
}
}
private:
Profiler* profiler_ = nullptr;
uint32_t event_handle_ = 0;
int64_t delegate_status_ = 0;
int64_t interpreter_status_ = 0;
};
} // namespace tflite
#define TFLITE_VARNAME_UNIQ_IMPL(name, ctr) name##ctr
#define TFLITE_VARNAME_UNIQ(name, ctr) TFLITE_VARNAME_UNIQ_IMPL(name, ctr)
#define TFLITE_SCOPED_TAGGED_DEFAULT_PROFILE(profiler, tag) \
tflite::ScopedProfile TFLITE_VARNAME_UNIQ(_profile_, __COUNTER__)( \
(profiler), (tag))
#define TFLITE_SCOPED_TAGGED_OPERATOR_PROFILE(profiler, tag, node_index) \
tflite::ScopedOperatorProfile TFLITE_VARNAME_UNIQ(_profile_, __COUNTER__)( \
(profiler), (tag), (node_index))
#define TFLITE_SCOPED_DELEGATE_OPERATOR_PROFILE(profiler, tag, node_index) \
tflite::ScopedDelegateOperatorProfile TFLITE_VARNAME_UNIQ( \
_profile_, __COUNTER__)((profiler), (tag), (node_index))
#define TFLITE_SCOPED_DELEGATE_PROFILED_OPERATOR_PROFILE(profiler, tag, \
node_index) \
tflite::ScopedDelegateProfiledOperatorProfile TFLITE_VARNAME_UNIQ( \
_profile_, __COUNTER__)((profiler), (tag), (node_index))
#define TFLITE_ADD_RUNTIME_INSTRUMENTATION_EVENT( \
profiler, tag, event_metadata1, event_metadata2) \
do { \
if (profiler) { \
const auto handle = profiler->BeginEvent( \
tag, Profiler::EventType::GENERAL_RUNTIME_INSTRUMENTATION_EVENT, \
event_metadata1, event_metadata2); \
profiler->EndEvent(handle); \
} \
} while (false);
#endif // TENSORFLOW_LITE_CORE_API_PROFILER_H_
+50
View File
@@ -0,0 +1,50 @@
/* 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/core/api/tensor_utils.h"
#include <string.h>
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
TfLiteStatus ResetVariableTensor(TfLiteTensor* tensor) {
if (!tensor->is_variable) {
return kTfLiteOk;
}
// TODO(b/115961645): Implement - If a variable tensor has a buffer, reset it
// to the value of the buffer.
int value = 0;
if (tensor->type == kTfLiteInt8) {
value = tensor->params.zero_point;
}
// TODO(b/139446230): Provide a platform header to better handle these
// specific scenarios.
#if defined(__ANDROID__) || defined(__x86_64__) || defined(__i386__) || \
defined(__i386) || defined(__x86__) || defined(__X86__) || \
defined(_X86_) || defined(_M_IX86) || defined(_M_X64)
memset(tensor->data.raw, value, tensor->bytes);
#else
char* raw_ptr = tensor->data.raw;
for (size_t i = 0; i < tensor->bytes; ++i) {
*raw_ptr = value;
raw_ptr++;
}
#endif
return kTfLiteOk;
}
} // namespace tflite
+28
View File
@@ -0,0 +1,28 @@
/* 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_CORE_API_TENSOR_UTILS_H_
#define TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
// Resets a variable tensor to the default value.
TfLiteStatus ResetVariableTensor(TfLiteTensor* tensor);
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_API_TENSOR_UTILS_H_
+23
View File
@@ -0,0 +1,23 @@
/* 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.
==============================================================================*/
/// \file
///
/// Abstract interface for verifying a model.
#ifndef TENSORFLOW_LITE_CORE_API_VERIFIER_H_
#define TENSORFLOW_LITE_CORE_API_VERIFIER_H_
#include "tensorflow/compiler/mlir/lite/core/api/verifier.h" // IWYU pragma: export
#endif // TENSORFLOW_LITE_CORE_API_VERIFIER_H_
+137
View File
@@ -0,0 +1,137 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite/core/shims:cc_library_with_tflite.bzl", "cc_library_with_tflite")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "async_kernel_internal",
hdrs = ["async_kernel_internal.h"],
compatible_with = get_compatible_with_portable(),
deps = [
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
cc_library(
name = "task_internal",
srcs = ["task_internal.cc"],
hdrs = ["task_internal.h"],
compatible_with = get_compatible_with_portable(),
deps = [
":async_kernel_internal",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
cc_test(
name = "task_internal_test",
srcs = ["task_internal_test.cc"],
deps = [
":task_internal",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/async/interop/c:types",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "async_subgraph",
srcs = ["async_subgraph.cc"],
hdrs = ["async_subgraph.h"],
compatible_with = get_compatible_with_portable(),
deps = [
":async_kernel_internal",
":task_internal",
"//tensorflow/lite:minimal_logging",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
cc_test(
name = "async_subgraph_test",
srcs = ["async_subgraph_test.cc"],
deps = [
":async_kernel_internal",
":async_subgraph",
":backend_async_kernel_interface",
":task_internal",
"//tensorflow/lite:framework",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/async/interop:attribute_map_internal",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/async/testing:mock_async_kernel",
"//tensorflow/lite/core/async/testing:test_backend",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/core/kernels:builtin_ops",
"@com_google_googletest//:gtest_main",
],
)
cc_library_with_tflite(
name = "backend_async_kernel_interface",
hdrs = ["backend_async_kernel_interface.h"],
deprecation = "Use //tensorflow/lite/async:backend_async_kernel_interface instead.",
tflite_deps = [
"//tensorflow/lite/async:backend_async_kernel_interface",
],
)
cc_library(
name = "async_signature_runner",
srcs = ["async_signature_runner.cc"],
hdrs = ["async_signature_runner.h"],
compatible_with = get_compatible_with_portable(),
deps = [
":async_kernel_internal",
":async_subgraph",
":task_internal",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/internal:signature_def",
],
)
cc_test(
name = "async_signature_runner_test",
srcs = ["async_signature_runner_test.cc"],
deps = [
":async_kernel_internal",
":async_signature_runner",
":backend_async_kernel_interface",
"//tensorflow/lite:framework",
"//tensorflow/lite:interpreter_test_util",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core/async/c:task",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/async/interop:attribute_map_internal",
"//tensorflow/lite/core/async/testing:mock_async_kernel",
"//tensorflow/lite/core/async/testing:test_backend",
"//tensorflow/lite/core/c:c_api_experimental",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/core/kernels:builtin_ops",
"@com_google_googletest//:gtest_main",
],
)
+13
View File
@@ -0,0 +1,13 @@
# TfLite asynchronous execution
WARNING: This feature is experimental and subject to change.
Experimental support for TFLite asynchronous execution and interoperability.
## Directory structure
| Directory | Description |
| ------------------ | --------------------------------------------------- |
| `/async` | Asynchronous execution APIs. Definition for async kernel. |
| `/async/interop` | Data structures supporting buffer and sync object interop. Reconciliation functions for buffer / sync attributes. |
| `/async/interop/c` | C APIs for buffer and sync object interop. |
@@ -0,0 +1,162 @@
/* 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_CORE_ASYNC_ASYNC_KERNEL_INTERNAL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_ASYNC_KERNEL_INTERNAL_H_
#include <cstddef>
#include <cstdint>
#include <vector>
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
typedef struct TfLiteAttributeMap TfLiteAttributeMap;
typedef struct TfLiteBackendBuffer TfLiteBackendBuffer;
typedef struct TfLiteExecutionTask TfLiteExecutionTask;
struct TfLiteAsyncKernel {
// Stores the arbitrary data used to identify the async kernel it self.
// Filled by the backend delegate.
void* kernel_data;
// Buffer operations
// ======================
// Registers the TfLiteBackendBuffer to `handle`.
// `buffer` and `attrs` lifespan is not guaranteed after the function call.
// kernels should read the stored attributes instead of caching the
// attribute map.
// `io_type` specifies whether this buffer is used as an input buffer
// or an output buffer. If a buffer is both used as input and output,
// specify it as output. Not null.
// `attrs` describes the attributes of the buffer. It's guaranteed to be
// of kTfLiteAttrMapTypeBuffer type and not null.
// `handle` is the buffer handle assigned by TfLite runtime to recognize
// this piece of buffer.
TfLiteStatus (*register_buffer)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle handle) = nullptr;
// Registers a buffer slice from a previously registered TfLiteBackendBuffer.
// `buffer` is the handle of the buffer pool previously registered.
// `attrs` contains the information of the buffer slice.
// `handle` is the buffer handle assigned by TfLite runtime to recognize
// this piece of buffer.
// If the `handle` is not recognized, returns error.
TfLiteStatus (*register_buffer_slice)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle handle) = nullptr;
// Unregisters a buffer or a buffer slice.
// `handle` is a buffer handle previously assigned via register_* calls.
// If the `handle` is not recognized, returns error.
TfLiteStatus (*unregister_buffer)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle handle) = nullptr;
// Reconciliations
// ===================
// Inspects the buffer object types supported by the backend.
// `io_type` specify whether the call returns supported input or output
// buffer.
// Note: the lifespan of returned *`type` strings should be tied to that
// of the backend delegate.
// Caller DOES NOT own returned types array.
void (*supported_buffer_types)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types,
size_t* n_types) = nullptr;
// Inspects the sync object types supported by the backend.
// `io_type` specify whether the call returns supported input or output
// sync object.
// Note: the lifespan of returned *`type` strings should be tied to that
// of the backend delegate.
// Caller DOES NOT own returned types array.
void (*supported_synchronizations)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types,
size_t* n_types) = nullptr;
// Reconciles buffer or sync attributes for tensor at tensor_index.
// Fills `merged` with reconciled attributes.
// If `conflict` is provided, conflicting attributes will be provided there.
// Returns true if there's no conflict.
bool (*reconcile_restrictions)(
const TfLiteAsyncKernel* async_kernel, const TfLiteOpaqueContext* context,
const TfLiteOpaqueNode* node, int tensor_index,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) = nullptr;
// Sets the input / output buffer / sync attributes.
// Backend kernel will check the input attributes covers all the requirements.
// A typical workflow is for callers call Reconcile*Restrictions method
// above to have a merged attribute list, check all restrictions are met
// and set input / output attribute here.
// Returns TfLiteOk if provided `attrs` covers all requirements.
TfLiteStatus (*set_attributes)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node, int tensor_index,
const TfLiteAttributeMap* attrs) = nullptr;
// Set attributes to the buffer, backend kernel will validate the buffer.
TfLiteStatus (*set_buffer_attributes)(
TfLiteAsyncKernel* async_kernel, const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs) = nullptr;
// Get attributes from the buffer, backend kernel will validate the buffer.
TfLiteStatus (*get_buffer_attributes)(TfLiteAsyncKernel* async_kernel,
const TfLiteBackendBuffer* buffer,
TfLiteAttributeMap* attrs) = nullptr;
// Prepares the kernel using the information from Set[In|Out]putAttributes
// call above.
TfLiteStatus (*prepare)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node) = nullptr;
// Execution methods
// =============================
// Schedules an execution with the information provided in task.
// The application is responsible for filling out buffer and sync mappings
// to tensors.
// Backend will set the sync ptr for related tensors if requested.
// i.e. SetOutputAttributes has sync implementation requested, and
// the TfLiteSynchronization is not null for the tensor in `task`.
// Returns kTfLiteOk if the execution is successfully scheduled.
TfLiteStatus (*eval)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context, TfLiteOpaqueNode* node,
TfLiteExecutionTask* task) = nullptr;
// Waits on the execution scheduled using the task to finish.
// Returns kTfLiteOk if the task is finished (w/ or w/o blocking).
TfLiteStatus (*wait)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task) = nullptr;
// Finishes the task and clean up allocated resources for the task.
// May block if there's pending executions.
// Returns kTfLiteOk if there's no error.
TfLiteStatus (*finish)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task) = nullptr;
};
#endif // TENSORFLOW_LITE_CORE_ASYNC_ASYNC_KERNEL_INTERNAL_H_
@@ -0,0 +1,193 @@
/* 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/core/async/async_signature_runner.h"
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/async_subgraph.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/task_internal.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/subgraph.h"
namespace tflite {
namespace async {
namespace {
// Returns the tensor index of the given signature name.
// `map` is a mapping from tensor signature name to tensor index.
// Return -1 if name is not found in the map or map is nullptr.
int GetIndex(const std::map<std::string, uint32_t>* map, const char* name) {
if (map == nullptr) return -1;
const auto& it = map->find(name);
return it == map->end() ? -1 : it->second;
}
} // namespace
int AsyncSignatureRunner::GetTensorIndex(TfLiteIoType io_type,
const char* name) const {
int tensor_index = -1;
switch (io_type) {
case kTfLiteIoTypeInput: {
tensor_index = GetIndex(input_to_index_, name);
break;
};
case kTfLiteIoTypeOutput: {
tensor_index = GetIndex(output_to_index_, name);
break;
}
default: {
return false;
}
}
if (tensor_index < 0) {
subgraph_->ReportError("Signature tensor name %s was not found", name);
}
return tensor_index;
}
AsyncSignatureRunner::AsyncSignatureRunner(
const internal::SignatureDef* signature_def, Subgraph* subgraph)
: subgraph_(subgraph) {
async_subgraph_ = std::make_unique<AsyncSubgraph>(subgraph);
if (signature_def) {
signature_key_ = signature_def->signature_key;
input_to_index_ = &signature_def->inputs;
output_to_index_ = &signature_def->outputs;
// Collects the list of input and output tensor names.
for (const auto& it : *input_to_index_) {
input_names_.push_back(it.first.c_str());
}
for (const auto& it : *output_to_index_) {
output_names_.push_back(it.first.c_str());
}
}
}
TfLiteStatus AsyncSignatureRunner::RegisterBuffer(
TfLiteIoType io_type, const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs, TfLiteBufferHandle* handle) {
return async_subgraph_->RegisterBuffer(io_type, buffer, attrs, handle);
}
TfLiteStatus AsyncSignatureRunner::RegisterBufferSlice(
TfLiteBufferHandle buffer_pool, const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle) {
return async_subgraph_->RegisterBufferSlice(buffer_pool, attrs, handle);
}
TfLiteStatus AsyncSignatureRunner::UnregisterBuffer(TfLiteBufferHandle handle) {
return async_subgraph_->UnregisterBuffer(handle);
}
const std::vector<const char*>& AsyncSignatureRunner::SupportedBufferTypes(
TfLiteIoType io_type) const {
return async_subgraph_->SupportedBufferTypes(io_type);
}
const std::vector<const char*>& AsyncSignatureRunner::SupportedSynchronizations(
TfLiteIoType io_type) const {
return async_subgraph_->SupportedSynchronizations(io_type);
}
bool AsyncSignatureRunner::ReconcileRestrictions(
TfLiteIoType io_type, const char* name,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) const {
auto tensor_index = GetTensorIndex(io_type, name);
return ReconcileRestrictions(tensor_index, user_provided_attributes, merged,
conflict);
}
bool AsyncSignatureRunner::ReconcileRestrictions(
int tensor_index, const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) const {
if (tensor_index < 0) return false;
return async_subgraph_->ReconcileRestrictions(
tensor_index, user_provided_attributes, merged, conflict);
}
TfLiteStatus AsyncSignatureRunner::SetAttributes(
TfLiteIoType io_type, const char* name, const TfLiteAttributeMap* attrs) {
auto tensor_index = GetTensorIndex(io_type, name);
return SetAttributes(tensor_index, attrs);
}
TfLiteStatus AsyncSignatureRunner::SetAttributes(
int tensor_index, const TfLiteAttributeMap* attrs) {
if (tensor_index < 0) return kTfLiteError;
return async_subgraph_->SetAttributes(tensor_index, attrs);
}
TfLiteStatus AsyncSignatureRunner::SetBufferAttributes(
const TfLiteBackendBuffer* buffer, const TfLiteAttributeMap* attrs) {
return async_subgraph_->SetBufferAttributes(buffer, attrs);
}
TfLiteStatus AsyncSignatureRunner::GetBufferAttributes(
const TfLiteBackendBuffer* buffer, TfLiteAttributeMap* attrs) {
return async_subgraph_->GetBufferAttributes(buffer, attrs);
}
TfLiteStatus AsyncSignatureRunner::PrepareBackends() {
return async_subgraph_->Prepare();
}
TfLiteExecutionTask* AsyncSignatureRunner::CreateTask() {
auto* task = async_subgraph_->CreateTask();
task->task->SetInputNameMap(input_to_index_);
task->task->SetOutputNameMap(output_to_index_);
return task;
}
TfLiteStatus AsyncSignatureRunner::InvokeAsync(TfLiteExecutionTask* task) {
return async_subgraph_->InvokeAsync(task);
}
TfLiteStatus AsyncSignatureRunner::Wait(TfLiteExecutionTask* task) {
return async_subgraph_->Wait(task);
}
TfLiteStatus AsyncSignatureRunner::Finish(TfLiteExecutionTask* task) {
return async_subgraph_->Finish(task);
}
const TfLiteOpaqueTensor* AsyncSignatureRunner::input_tensor(
const char* input_name) const {
if (auto idx = GetTensorIndex(kTfLiteIoTypeInput, input_name); idx >= 0) {
return reinterpret_cast<const TfLiteOpaqueTensor*>(subgraph_->tensor(idx));
}
subgraph_->ReportError("Input name %s was not found", input_name);
return nullptr;
}
const TfLiteOpaqueTensor* AsyncSignatureRunner::output_tensor(
const char* output_name) const {
if (auto idx = GetTensorIndex(kTfLiteIoTypeOutput, output_name); idx >= 0) {
return reinterpret_cast<const TfLiteOpaqueTensor*>(subgraph_->tensor(idx));
}
subgraph_->ReportError("Output name %s was not found", output_name);
return nullptr;
}
} // namespace async
} // namespace tflite
@@ -0,0 +1,256 @@
/* 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_CORE_ASYNC_ASYNC_SIGNATURE_RUNNER_H_
#define TENSORFLOW_LITE_CORE_ASYNC_ASYNC_SIGNATURE_RUNNER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/async_subgraph.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/subgraph.h"
#include "tensorflow/lite/internal/signature_def.h"
namespace tflite {
namespace async {
// Forward declarations
class AsyncSignatureRunnerTest;
// WARNING: Experimental interface, subject to change
//
// Async version of SignatureRunner class for running TFLite models using
// SignatureDef.
class AsyncSignatureRunner {
public:
// Builds the AsyncSignatureRunner given the provided signature_def and
// subgraph.
AsyncSignatureRunner(const internal::SignatureDef* signature_def,
Subgraph* subgraph);
// Registers a TfLiteBackendBuffer to backends.
// The `buffer` will be sent to all backends and TfLite runtime
// will assign an unique `handle` for backends to recognize the buffer.
// `io_type` specifies whether the buffer will be used as an input only
// or it will be used as an output.
// `buffer`, `attrs`, and `handle` should not be null.
// The application must provide the buffer type in `attrs`. It can also
// include additional attributes for the backends to validate (e.g. padding).
// Returns kTfLiteError is any of the backends failed to register
// the buffer (e.g. buffer type is not supported).
TfLiteStatus RegisterBuffer(TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
// Registers a buffer slice from a previously registered handle `buffer_pool`.
// `attrs` needs to contain both the information from the buffer pool
// as well as slice information (offset and size).
// `attrs` and `handle` should not be nullptr.
// If the application choose to provide the buffer type in `attrs` it must be
// identical to the buffer type of the buffer pool provided during
// RegisterBuffer call.
// Returns kTfLiteError if the registration failed (e.g. `buffer_pool`
// not found).
TfLiteStatus RegisterBufferSlice(TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
// Unregisters a buffer (or buffer slice) with `handle`.
// Returns kTfLiteError if `handle` is not recognized.
TfLiteStatus UnregisterBuffer(TfLiteBufferHandle handle);
// Returns a list of names of supported buffer types.
const std::vector<const char*>& SupportedBufferTypes(
TfLiteIoType io_type) const;
// Returns a list of names of supported synchronization types.
const std::vector<const char*>& SupportedSynchronizations(
TfLiteIoType io_type) const;
// Reconciles registrations with all backends depending on I/O tensor `name`
// if the backend kernel reads or writes the tensor.
// Merged attributes will be populated to `merged`.
// If there's a conflict attribute, it's populated to `conflict` if provided.
// `user_provided_attributes` and `merged` should not be nullptr.
// Returns true if the reconciliation successes and there's no conflicting
// attributes.
bool ReconcileRestrictions(TfLiteIoType io_type, const char* name,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged,
TfLiteAttributeMap* conflict) const;
// Reconciles registrations with all backends depending on I/O tensor at
// `tensor_index` if the backend kernel reads or writes the tensor. Merged
// attributes will be populated to `merged`. If there's a conflict attribute,
// it's populated to `conflict` if provided. `user_provided_attributes` and
// `merged` should not be nullptr.
// Returns true if the reconciliation successes and there's no conflicting
// attributes.
bool ReconcileRestrictions(int tensor_index,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged,
TfLiteAttributeMap* conflict) const;
// Finalizes the attribute for I/O tensor `name` with `attrs`.
// The attributes will be sent to all backend kernels that depends on tensor.
// Must call `Prepare` after setting new attributes.
// Returns true if all backends accept the `attrs`.
TfLiteStatus SetAttributes(TfLiteIoType io_type, const char* name,
const TfLiteAttributeMap* attrs);
// Finalizes the attribute for I/O tensor at `tensor_index` with `attrs`.
// The attributes will be sent to all backend kernels that depends on tensor.
// Must call `Prepare` after setting new attributes.
// Returns true if all backends accept the `attrs`.
TfLiteStatus SetAttributes(int tensor_index, const TfLiteAttributeMap* attrs);
// Set the attributes of a specific buffer. Returns
// kTfLiteDelegateError if the buffer is not registered.
TfLiteStatus SetBufferAttributes(const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs);
// Get the attributes from a specific buffer. Returns
// kTfLiteDelegateError if the buffer has not been found in the
// backends.
TfLiteStatus GetBufferAttributes(const TfLiteBackendBuffer* buffer,
TfLiteAttributeMap* attrs);
// Prepares delegate backends for execution.
// Must be called after calling `SetAttributes`.
TfLiteStatus PrepareBackends();
// Creates an execution task for this subgraph.
// Must be called after `Prepare`.
// When creating task, all intermediate resources will be allocated
// for this task.
// The task must be released by calling `Finish`.
TfLiteExecutionTask* CreateTask();
// Schedules an asynchronous execution with I/O information
// provided in `task`.
// `task` should not be nullptr.
// Returns kTfLiteError if any backend kernels failed to schedule
// the execution.
TfLiteStatus InvokeAsync(TfLiteExecutionTask* task);
// Blocks and wait for execution tied to `task` to finish.
// `task` should not be nullptr.
// Can be called from multiple threads. All calls will block until the
// task finishes execution.
//
// NOTE: `Wait` and `InvokeAsync` should be called in pairs with the same
// `task`, unless `Finish(task)` is called and task is freed. The application
// is responsible to call `Wait` after `InvokeAsync` even if all output
// tensors are associated with synchronizations.
//
// Returns kTfLiteError if any backends failed to finish the execution.
// If the task is currently idle, it will return its latest status code.
TfLiteStatus Wait(TfLiteExecutionTask* task);
// Finishes the task and release all intermediate resources tied to
// this task. Must be and only be called once for the same `task` object.
// If there's ongoing execution, will block wait for the execution
// to finish.
// `task` should not be nullptr and will be deleted.
// NOTE: Caller needs to ensure `Finish` is not called concurrently with
// `InvokeAsync` or `Wait`.
// Returns kTfLiteError if failes to release the task. The task will be
// destroyed regardless of error or not.
TfLiteStatus Finish(TfLiteExecutionTask* task);
/// Returns the key for the corresponding signature.
const std::string& signature_key() { return signature_key_; }
/// Returns the number of inputs.
size_t input_size() const { return subgraph_->inputs().size(); }
/// Returns the number of outputs.
size_t output_size() const { return subgraph_->outputs().size(); }
/// Read-only access to list of signature input names.
/// Returns an empty vector if the model does not have signature.
const std::vector<const char*>& input_names() { return input_names_; }
/// Read-only access to list of signature output names.
/// Returns an empty vector if the model does not have signature.
const std::vector<const char*>& output_names() { return output_names_; }
/// Returns the input tensor information identified by 'input_name' in the
/// given signature. Returns nullptr if the given name is not valid.
/// Note: The returned `TfLiteTensor` should only be used to retrieve
/// tensor metadata (dimension, data type, etc.). Tensor data should only be
/// accessed via hardware buffer directly.
const TfLiteOpaqueTensor* input_tensor(const char* input_name) const;
/// Returns the output tensor information identified by 'output_name' in the
/// given signature. Returns nullptr if the given name is not valid.
/// Note: The returned `TfLiteTensor` should only be used to retrieve
/// tensor metadata (dimension, data type, etc.). Tensor data should only be
/// accessed via hardware buffer directly.
const TfLiteOpaqueTensor* output_tensor(const char* output_name) const;
/// Tensor index based accessors.
/// Read only access to list of input index.
const std::vector<int>& inputs() const { return subgraph_->inputs(); }
/// Read only access to list of output index.
const std::vector<int>& outputs() const { return subgraph_->outputs(); }
/// Returns the tensor information by tensor index.
const TfLiteOpaqueTensor* tensor(int tensor_index) const {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueTensor and TfLiteTensor being equivalent.
return reinterpret_cast<const TfLiteOpaqueTensor*>(
subgraph_->tensor(tensor_index));
}
private:
friend class AsyncSignatureRunnerTest;
int GetTensorIndex(TfLiteIoType io_type, const char* name) const;
std::string signature_key_;
// The list of input tensor names.
std::vector<const char*> input_names_;
// The list of output tensor names.
std::vector<const char*> output_names_;
// Not owned.
// If the model does not have signature def, the name maps will be nullptr.
const std::map<std::string, uint32_t>* input_to_index_ = nullptr;
const std::map<std::string, uint32_t>* output_to_index_ = nullptr;
// Not owned.
Subgraph* subgraph_ = nullptr;
// Currently AsyncSubgraph is owned by SignatureRunner. However after
// we stabilize the interface, the async subgraph should be owned by the
// interpreter and AsyncSignatureRunner won't own any of the subgraphs.
std::unique_ptr<AsyncSubgraph> async_subgraph_;
};
} // namespace async
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_ASYNC_SIGNATURE_RUNNER_H_
@@ -0,0 +1,239 @@
/* 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/core/async/async_signature_runner.h"
#include <cstdlib>
#include <memory>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/backend_async_kernel_interface.h"
#include "tensorflow/lite/core/async/c/task.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/testing/mock_async_kernel.h"
#include "tensorflow/lite/core/async/testing/test_backend.h"
#include "tensorflow/lite/core/c/c_api_opaque.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/core/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/interpreter_test_util.h"
using ::testing::_;
using ::testing::Return;
namespace tflite {
namespace async {
class AsyncSignatureRunnerTest : public InterpreterTest {
protected:
void SetUp() override {
InitInterpreter();
const char kSignatureKey[] = "serving_default";
BuildSignature(kSignatureKey, {{"input", 0}}, {{"output", 1}});
}
void InitInterpreter() {
kernel_ =
std::make_unique<::testing::StrictMock<testing::MockAsyncKernel>>();
backend_ = std::make_unique<testing::TestBackend>(kernel_->kernel());
interpreter_ = std::make_unique<Interpreter>();
interpreter_->AddTensors(2);
interpreter_->SetInputs({0});
interpreter_->SetOutputs({1});
TfLiteQuantizationParams quant;
interpreter_->SetTensorParametersReadWrite(0, kTfLiteFloat32, "x", {3},
quant);
interpreter_->SetTensorParametersReadWrite(1, kTfLiteFloat32, "a", {3},
quant);
TfLiteRegistration* reg = ops::builtin::Register_ADD();
void* builtin_data_1 = malloc(sizeof(int));
interpreter_->AddNodeWithParameters({0, 0}, {1}, nullptr, 0, builtin_data_1,
reg);
interpreter_->ModifyGraphWithDelegate(backend_->get_delegate());
}
int GetTensorIndex(TfLiteIoType io_type, const char* name) {
return signature_runner_->GetTensorIndex(io_type, name);
}
protected:
std::unique_ptr<::testing::StrictMock<testing::MockAsyncKernel>> kernel_;
std::unique_ptr<testing::TestBackend> backend_;
internal::SignatureDef signature_def_;
AsyncSignatureRunner* signature_runner_ = nullptr;
};
TEST_F(AsyncSignatureRunnerTest, GetAsyncSignatureRunner) {
EXPECT_EQ(nullptr, signature_runner_);
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_NE(nullptr, signature_runner_);
auto* signature_runner_null_key =
interpreter_->GetAsyncSignatureRunner(nullptr);
EXPECT_EQ(signature_runner_null_key, signature_runner_);
EXPECT_STREQ("serving_default", signature_runner_->signature_key().c_str());
EXPECT_EQ(nullptr, interpreter_->GetAsyncSignatureRunner("foo"));
}
TEST_F(AsyncSignatureRunnerTest, WrongSignatureKeyTest) {
const char kSignatureKey[] = "serving_default";
BuildSignature(interpreter_.get(), kSignatureKey, {{"input", 0}},
{{"output", 1}}, 1);
signature_runner_ = interpreter_->GetAsyncSignatureRunner(nullptr);
EXPECT_EQ(nullptr, signature_runner_);
}
TEST_F(AsyncSignatureRunnerTest, InputsTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_EQ(1, signature_runner_->input_size());
auto* input_name = signature_runner_->input_names()[0];
EXPECT_STREQ("input", input_name);
EXPECT_STREQ(
"x", TfLiteOpaqueTensorName(signature_runner_->input_tensor(input_name)));
}
TEST_F(AsyncSignatureRunnerTest, OutputsTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_EQ(1, signature_runner_->output_size());
auto* output_name = signature_runner_->output_names()[0];
EXPECT_STREQ("output", output_name);
EXPECT_STREQ("a", TfLiteOpaqueTensorName(
signature_runner_->output_tensor(output_name)));
}
TEST_F(AsyncSignatureRunnerTest, InputNameTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_EQ(0, GetTensorIndex(kTfLiteIoTypeInput, "input"));
EXPECT_EQ(-1, GetTensorIndex(kTfLiteIoTypeInput, "output"));
EXPECT_EQ(-1, GetTensorIndex(kTfLiteIoTypeInput, "foo"));
}
TEST_F(AsyncSignatureRunnerTest, OutputNameTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_EQ(1, GetTensorIndex(kTfLiteIoTypeOutput, "output"));
EXPECT_EQ(-1, GetTensorIndex(kTfLiteIoTypeOutput, "input"));
EXPECT_EQ(-1, GetTensorIndex(kTfLiteIoTypeOutput, "foo"));
}
TEST_F(AsyncSignatureRunnerTest, CreateTaskTest) {
EXPECT_CALL(*kernel_, Finish(::testing::_, ::testing::_));
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
auto* task = signature_runner_->CreateTask();
EXPECT_NE(nullptr, task);
TfLiteExecutionTaskSetBuffer(task, kTfLiteIoTypeInput, "input", 24);
TfLiteExecutionTaskSetBuffer(task, kTfLiteIoTypeOutput, "output", 12);
TfLiteBufferHandle input_buffer, output_buffer;
input_buffer = TfLiteExecutionTaskGetBufferByIndex(task, 0);
output_buffer = TfLiteExecutionTaskGetBufferByIndex(task, 1);
EXPECT_EQ(24, input_buffer);
EXPECT_EQ(12, output_buffer);
EXPECT_EQ(kTfLiteOk, signature_runner_->Finish(task));
}
TEST_F(AsyncSignatureRunnerTest, ReconcileTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner("serving_default");
EXPECT_CALL(*kernel_, ReconcileRestrictions(_, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*kernel_, SetAttributes(_, _, _, _)).WillOnce(Return(kTfLiteOk));
auto* attrs = new TfLiteAttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_TRUE(signature_runner_->ReconcileRestrictions(
kTfLiteIoTypeInput, "input", attrs, attrs, attrs));
EXPECT_EQ(kTfLiteOk, signature_runner_->SetAttributes(kTfLiteIoTypeInput,
"input", attrs));
EXPECT_FALSE(signature_runner_->ReconcileRestrictions(
kTfLiteIoTypeInput, "foo", attrs, attrs, attrs));
EXPECT_EQ(kTfLiteError,
signature_runner_->SetAttributes(kTfLiteIoTypeInput, "foo", attrs));
delete attrs;
}
class AsyncSignatureRunnerNoSignatureDefTest : public AsyncSignatureRunnerTest {
public:
void SetUp() override { InitInterpreter(); }
};
TEST_F(AsyncSignatureRunnerNoSignatureDefTest, GetAsyncSignatureRunner) {
EXPECT_EQ(nullptr, signature_runner_);
EXPECT_NE(nullptr, interpreter_->GetAsyncSignatureRunner(nullptr));
EXPECT_EQ(nullptr, interpreter_->GetAsyncSignatureRunner("foo"));
}
TEST_F(AsyncSignatureRunnerNoSignatureDefTest, InputsTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner(nullptr);
EXPECT_EQ(1, signature_runner_->input_size());
EXPECT_EQ(1, signature_runner_->input_names().size());
EXPECT_EQ(1, signature_runner_->inputs().size());
EXPECT_NE(nullptr, signature_runner_->tensor(signature_runner_->inputs()[0]));
}
TEST_F(AsyncSignatureRunnerNoSignatureDefTest, OutputsTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner(nullptr);
EXPECT_EQ(1, signature_runner_->output_size());
EXPECT_EQ(1, signature_runner_->output_names().size());
EXPECT_EQ(1, signature_runner_->outputs().size());
EXPECT_NE(nullptr,
signature_runner_->tensor(signature_runner_->outputs()[0]));
}
TEST_F(AsyncSignatureRunnerNoSignatureDefTest, CreateTaskTest) {
EXPECT_CALL(*kernel_, Finish(::testing::_, ::testing::_));
signature_runner_ = interpreter_->GetAsyncSignatureRunner(nullptr);
auto* task = signature_runner_->CreateTask();
EXPECT_NE(nullptr, task);
TfLiteExecutionTaskSetBufferByIndex(task, 0, 24);
TfLiteExecutionTaskSetBufferByIndex(task, 1, 12);
TfLiteBufferHandle input_buffer, output_buffer;
input_buffer = TfLiteExecutionTaskGetBufferByIndex(task, 0);
output_buffer = TfLiteExecutionTaskGetBufferByIndex(task, 1);
EXPECT_EQ(24, input_buffer);
EXPECT_EQ(12, output_buffer);
EXPECT_EQ(kTfLiteOk, signature_runner_->Finish(task));
}
TEST_F(AsyncSignatureRunnerNoSignatureDefTest, ReconcileTest) {
signature_runner_ = interpreter_->GetAsyncSignatureRunner(nullptr);
EXPECT_CALL(*kernel_, ReconcileRestrictions(_, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*kernel_, SetAttributes(_, _, _, _)).WillOnce(Return(kTfLiteOk));
auto* attrs = new TfLiteAttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_TRUE(signature_runner_->ReconcileRestrictions(0, attrs, attrs, attrs));
EXPECT_EQ(kTfLiteOk, signature_runner_->SetAttributes(0, attrs));
EXPECT_FALSE(
signature_runner_->ReconcileRestrictions(42, attrs, attrs, attrs));
EXPECT_EQ(kTfLiteError, signature_runner_->SetAttributes(42, attrs));
delete attrs;
}
} // namespace async
} // namespace tflite
@@ -0,0 +1,250 @@
/* 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/core/async/async_subgraph.h"
#include <atomic>
#include <cstddef>
#include <vector>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/task_internal.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/subgraph.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
namespace tflite {
namespace async {
namespace {
TfLiteAsyncKernel* GetAsyncKernel(TfLiteContext* context,
const TfLiteRegistration& op_reg,
TfLiteNode& node) {
if (op_reg.registration_external) {
// The casts here are only safe because this code is part of TFLite
// runtime. Applications should not rely on TfLiteContext / TfLiteNode being
// equivalent to TfLiteOpaqueContext / TfLiteOpaqueNode.
auto* context_ = reinterpret_cast<TfLiteOpaqueContext*>(context);
auto* node_ = reinterpret_cast<TfLiteOpaqueNode*>(&node);
if (op_reg.registration_external->async_kernel_with_data) {
auto user_data = op_reg.registration_external->user_data;
return op_reg.registration_external->async_kernel_with_data(
user_data, context_, node_);
} else if (op_reg.registration_external->async_kernel) {
return op_reg.registration_external->async_kernel(context_, node_);
}
}
if (op_reg.async_kernel) {
return op_reg.async_kernel(context, &node);
}
return nullptr;
}
} // namespace
Subgraph* AsyncSubgraph::subgraph() const { return subgraph_; }
TfLiteContext* AsyncSubgraph::context() const { return subgraph_->context(); }
TfLiteOpaqueContext* AsyncSubgraph::opaque_context() const {
// The casts here are only safe because this code is part of TFLite
// runtime. Applications should not rely on TfLiteContext
// being equivalent to TfLiteOpaqueContext.
return reinterpret_cast<TfLiteOpaqueContext*>(context());
}
TfLiteAsyncKernel* AsyncSubgraph::async_kernel() const { return async_kernel_; }
AsyncSubgraph::AsyncSubgraph(Subgraph* subgraph) : subgraph_(subgraph) {
// Currently we only support one delegate and fully delegated subgraph.
if (!IsFullyDelegated()) {
subgraph->ReportError("Model is not fully delegated by 1 backend.");
return;
}
// Ensured by `IsFullyDelegated`, there's only 1 node in execution plan.
auto node_index = subgraph_->execution_plan()[0];
TfLiteNode& node = subgraph_->nodes_and_registration_[node_index].first;
const TfLiteRegistration& registration =
subgraph_->nodes_and_registration_[node_index].second;
async_kernel_ = GetAsyncKernel(context(), registration, node);
if (!async_kernel_) {
subgraph->ReportError("Backend does not support asynchronous execution.");
return;
}
// TODO(b/191883048): Add AsyncSubgraph as friend class of Subgraph and
// remove the const cast.
opaque_node_ =
reinterpret_cast<TfLiteOpaqueNode*>(const_cast<TfLiteNode*>(&node));
#define POPULATE_VECTOR(io_type, accessor, dest) \
{ \
const char* const* types = nullptr; \
size_t n_types = 0; \
(*async_kernel_->accessor)(async_kernel_, io_type, &types, &n_types); \
dest[io_type] = std::vector<const char*>(types, types + n_types); \
}
POPULATE_VECTOR(kTfLiteIoTypeInput, supported_buffer_types,
supported_buffer_types_);
POPULATE_VECTOR(kTfLiteIoTypeOutput, supported_buffer_types,
supported_buffer_types_);
POPULATE_VECTOR(kTfLiteIoTypeInput, supported_synchronizations,
supported_synchronizations_);
POPULATE_VECTOR(kTfLiteIoTypeOutput, supported_synchronizations,
supported_synchronizations_);
#undef POPULATE_VECTOR
}
bool AsyncSubgraph::IsFullyDelegated() const {
if (subgraph_->execution_plan().size() != 1) return false;
const TfLiteNode& node =
subgraph_->nodes_and_registration()[subgraph_->execution_plan()[0]].first;
if (node.delegate == nullptr) return false;
return true;
}
TfLiteStatus AsyncSubgraph::RegisterBuffer(TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle) {
if (buffer == nullptr || attrs == nullptr || handle == nullptr ||
async_kernel() == nullptr) {
return kTfLiteError;
}
*handle = next_buffer_handle_.fetch_add(1, std::memory_order_relaxed);
return (*async_kernel_->register_buffer)(
async_kernel_, reinterpret_cast<TfLiteOpaqueContext*>(context()), io_type,
buffer, attrs, *handle);
}
TfLiteStatus AsyncSubgraph::RegisterBufferSlice(TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle) {
if (attrs == nullptr || handle == nullptr || async_kernel() == nullptr) {
return kTfLiteError;
}
*handle = next_buffer_handle_.fetch_add(1, std::memory_order_relaxed);
return (*async_kernel_->register_buffer_slice)(
async_kernel_, opaque_context(), buffer_pool, attrs, *handle);
}
TfLiteStatus AsyncSubgraph::UnregisterBuffer(TfLiteBufferHandle handle) {
if (async_kernel() == nullptr) return kTfLiteError;
return (*async_kernel_->unregister_buffer)(async_kernel_, opaque_context(),
handle);
}
const std::vector<const char*>& AsyncSubgraph::SupportedBufferTypes(
TfLiteIoType io_type) const {
return supported_buffer_types_.at(io_type);
}
const std::vector<const char*>& AsyncSubgraph::SupportedSynchronizations(
TfLiteIoType io_type) const {
return supported_synchronizations_.at(io_type);
}
bool AsyncSubgraph::ReconcileRestrictions(
int tensor_index, const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) const {
if (user_provided_attributes == nullptr || merged == nullptr ||
async_kernel() == nullptr) {
return false;
}
if (tensor_index < 0 || tensor_index >= subgraph_->tensors_size()) {
return false;
}
return (*async_kernel_->reconcile_restrictions)(
async_kernel_, opaque_context(), opaque_node_, tensor_index,
user_provided_attributes, merged, conflict);
}
TfLiteStatus AsyncSubgraph::SetAttributes(int tensor_index,
const TfLiteAttributeMap* attrs) {
if (attrs == nullptr || async_kernel() == nullptr) {
return kTfLiteError;
}
if (tensor_index < 0 || tensor_index >= subgraph_->tensors_size()) {
return kTfLiteError;
}
return (*async_kernel_->set_attributes)(async_kernel_, opaque_context(),
opaque_node_, tensor_index, attrs);
}
TfLiteStatus AsyncSubgraph::SetBufferAttributes(
const TfLiteBackendBuffer* buffer, const TfLiteAttributeMap* attrs) {
return (*async_kernel_->set_buffer_attributes)(async_kernel_, buffer, attrs);
}
TfLiteStatus AsyncSubgraph::GetBufferAttributes(
const TfLiteBackendBuffer* buffer, TfLiteAttributeMap* attrs) {
return (*async_kernel_->get_buffer_attributes)(async_kernel_, buffer, attrs);
}
TfLiteStatus AsyncSubgraph::Prepare() {
if (async_kernel() == nullptr) return kTfLiteError;
return (*async_kernel_->prepare)(async_kernel_, opaque_context(),
opaque_node_);
}
TfLiteExecutionTask* AsyncSubgraph::CreateTask() {
return new TfLiteExecutionTask;
}
TfLiteStatus AsyncSubgraph::InvokeAsync(TfLiteExecutionTask* task) {
if (task == nullptr || async_kernel() == nullptr) {
return kTfLiteError;
}
if (task->task->SetScheduled(true)) {
TFLITE_LOG(tflite::TFLITE_LOG_ERROR,
"The task has already been scheduled for execution.");
return kTfLiteError;
}
auto ret = (*async_kernel_->eval)(async_kernel_, opaque_context(),
opaque_node_, task);
task->task->SetStatus(ret);
return ret;
}
TfLiteStatus AsyncSubgraph::Wait(TfLiteExecutionTask* task) {
if (task == nullptr || async_kernel() == nullptr) {
return kTfLiteError;
}
if (!task->task->Scheduled()) {
// Nothing to wait. Returns the previous status code in case multiple
// threads are waiting for the same task.
return task->task->Status();
}
auto ret = (*async_kernel_->wait)(async_kernel_, opaque_context(), task);
task->task->SetStatus(ret);
task->task->SetScheduled(false);
return ret;
}
TfLiteStatus AsyncSubgraph::Finish(TfLiteExecutionTask* task) {
if (async_kernel() == nullptr) return kTfLiteError;
auto ret = (*async_kernel_->finish)(async_kernel_, opaque_context(), task);
if (ret != kTfLiteOk) {
subgraph_->ReportError("Failed to finish task.");
}
delete task;
return ret;
}
} // namespace async
} // namespace tflite
+198
View File
@@ -0,0 +1,198 @@
/* 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_CORE_ASYNC_ASYNC_SUBGRAPH_H_
#define TENSORFLOW_LITE_CORE_ASYNC_ASYNC_SUBGRAPH_H_
#include <atomic>
#include <map>
#include <vector>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/subgraph.h"
namespace tflite {
namespace async {
// Forward declaration
class AsyncSubgraphTestPeer;
// AsyncSubgraph class manages to dispatch I/O information and
// schedule executions to underlying delegate kernels.
// TODO(b/191883048): Currently we require either `AllocateTensors` or
// `EnsureTensorAllocation` called to ensure the backend kernels are prepared.
// However, we don't need to allocate the CPU memory for input / output tensors.
// We need customize the OpPrepare or memory planner to skip the allocation
// for user provided buffer case.
class AsyncSubgraph {
public:
explicit AsyncSubgraph(Subgraph* subgraph);
// Returns the underlying TfLite subgraph.
Subgraph* subgraph() const;
// Returns the TfLiteContext of the subgraph.
TfLiteContext* context() const;
// Registers a TfLiteBackendBuffer to backends.
// The `buffer` will be sent to all backends and TfLite runtime
// will assign an unique `handle` for backends to recognize the buffer.
// `buffer`, `attrs`, and `handle` should not be null.
// Returns kTfLiteError is any of the backends failed to register
// the buffer (e.g. buffer type is not supported).
TfLiteStatus RegisterBuffer(TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
// Registers a buffer slice from a previously registered handle `buffer_pool`.
// `attrs` needs to contain both the information from the buffer pool
// as well as slice information (offset and size).
// `attrs` and `handle` should not be nullptr.
//
// NOTE: When using sliced buffer as output buffer, the application needs to
// make sure slices from the same buffer pool should not be used across
// different executions (from InvokeAsync call to the output sync signals)
// otherwise data corruption may occur.
// TODO(b/243175542): Programmatically ensure slices from one buffer are used
// exclusively by one backend to write to for a single execution.
//
// Returns kTfLiteError if the registration failed (e.g. `buffer_pool`
// not found).
TfLiteStatus RegisterBufferSlice(TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
// Unregisters a buffer (or buffer slice) with `handle`.
// Returns kTfLiteError if `handle` is not recognized.
TfLiteStatus UnregisterBuffer(TfLiteBufferHandle handle);
// Returns a list of names of supported buffer types.
const std::vector<const char*>& SupportedBufferTypes(
TfLiteIoType io_type) const;
// Returns a list of names of supported synchronization types.
const std::vector<const char*>& SupportedSynchronizations(
TfLiteIoType io_type) const;
// Reconciles registrations with all backends depending on tensor at
// `tensor_index` if the backend kernel reads or writes the tensor.
// Merged attributes will be populated to `merged`.
// If there's a conflict attribute, it's populated to `conflict` if provided.
// `user_provided_attributes` and `merged` should not be nullptr.
// Returns true if the reconcilation successes and there's no conflicting
// attributes.
bool ReconcileRestrictions(int tensor_index,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged,
TfLiteAttributeMap* conflict) const;
// Finalizes the attribute for tensor at `tensor_index` with `attrs`.
// The attributes will be sent to all backend kernels that depends on tensor
// at `tensor_index`.
// Must call `Prepare` after setting new attributes.
// Returns true if all backends accept the `attrs`.
TfLiteStatus SetAttributes(int tensor_index, const TfLiteAttributeMap* attrs);
// Set the attributes for a specific buffer. `attrs` should be initialized
// before calling this function and could be constructed by calling
// TfLiteAttributeMapCreate(). The attributes will be sent to backend kernels
// and stored in the map with the buffer. `buffer` and `attrs` should not be
// nullptr. The buffer needs to be registered before calling this function.
TfLiteStatus SetBufferAttributes(const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs);
// Get the attributes for a specific buffer. `attrs` should be initialized
// before calling this function and could be constructed by calling
// TfLiteAttributeMapCreate(). `attrs` will be used to store the attributes
// obtained from the backend kernel. If `attrs` is a non-empty map, it will be
// overwritten by the attributes of the buffer. `buffer` and `attrs` should
// not be nullptr. The buffer needs to be registered before calling this
// function.
TfLiteStatus GetBufferAttributes(const TfLiteBackendBuffer* buffer,
TfLiteAttributeMap* attrs);
// Prepares delegate backends for execution.
// Must be called after calling `SetAttributes`.
TfLiteStatus Prepare();
// Creates an execution task for this subgraph.
// Must be called after `Prepare`.
// When creating task, all intermediate resources will be allocated
// for this task.
// The task must be released by calling `Finish`.
TfLiteExecutionTask* CreateTask();
// Schedules an asynchronous execution with I/O information
// provided in `task`.
// `task` should not be nullptr.
// Returns kTfLiteError if any backend kernels failed to schedule
// the execution.
TfLiteStatus InvokeAsync(TfLiteExecutionTask* task);
// Blocks and wait for execution tied to `task` to finish.
// `task` should not be nullptr.
// Returns kTfLiteError if any backends failed to finish the execution.
// If the task is currently idle, it will return its latest status code.
TfLiteStatus Wait(TfLiteExecutionTask* task);
// Finishes the task and release all intermediate resources tied to
// this task.
// If there's ongoing execution, will block wait for the execution
// to finish.
// `task` should not be nullptr and will be deleted.
// Returns kTfLiteError if failes to release the task. In this case `task`
// will not be deleted.
TfLiteStatus Finish(TfLiteExecutionTask* task);
private:
friend class AsyncSubgraphTestPeer;
// Returns true if the subgraph is fully delegated by 1 backend.
bool IsFullyDelegated() const;
// Returns the opaque TfLiteContext of the subgraph.
TfLiteOpaqueContext* opaque_context() const;
// Returns the async backend kernel that delegates the subgraph.
// NOTE: Since we assume only 1 backend will delegate the model, we cache
// the async kernel instance. In theory, the subgraph should iterate through
// execution plan to fetch the individual async kernels and operate
// respectively.
TfLiteAsyncKernel* async_kernel() const;
// Not owned.
Subgraph* subgraph_ = nullptr;
// Next buffer handle to assign in Register* calls.
std::atomic<TfLiteBufferHandle> next_buffer_handle_ = {0};
// Supported buffer and sync types.
std::map<TfLiteIoType, std::vector<const char*>> supported_buffer_types_;
std::map<TfLiteIoType, std::vector<const char*>> supported_synchronizations_;
// Currently AsyncSubgraph only support fully delegated by 1 backend case.
// Not owned.
mutable TfLiteAsyncKernel* async_kernel_ = nullptr;
TfLiteOpaqueNode* opaque_node_ = nullptr;
};
} // namespace async
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_ASYNC_SUBGRAPH_H_
@@ -0,0 +1,168 @@
/* 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/core/async/async_subgraph.h"
#include <cstdlib>
#include <memory>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/backend_async_kernel_interface.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/async/task_internal.h"
#include "tensorflow/lite/core/async/testing/mock_async_kernel.h"
#include "tensorflow/lite/core/async/testing/test_backend.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/core/kernels/builtin_op_kernels.h"
using ::testing::_;
namespace tflite {
namespace async {
class AsyncSubgraphTestPeer {
public:
explicit AsyncSubgraphTestPeer(AsyncSubgraph* subgraph)
: subgraph_(subgraph) {}
bool IsFullyDelegated() const { return subgraph_->IsFullyDelegated(); }
private:
AsyncSubgraph* subgraph_;
};
class AsyncSubgraphTest : public ::testing::Test {
protected:
void SetUp() override {
kernel_ = std::make_unique<testing::MockAsyncKernel>();
backend_ = std::make_unique<testing::TestBackend>(kernel_->kernel());
interpreter_ = std::make_unique<Interpreter>();
interpreter_->AddTensors(5);
interpreter_->SetInputs({0, 1});
interpreter_->SetOutputs({3, 4});
TfLiteQuantizationParams quant;
interpreter_->SetTensorParametersReadWrite(0, kTfLiteFloat32, "", {3},
quant);
interpreter_->SetTensorParametersReadWrite(1, kTfLiteFloat32, "", {3},
quant);
interpreter_->SetTensorParametersReadWrite(2, kTfLiteFloat32, "", {3},
quant);
interpreter_->SetTensorParametersReadWrite(3, kTfLiteFloat32, "", {3},
quant);
interpreter_->SetTensorParametersReadWrite(4, kTfLiteFloat32, "", {3},
quant);
TfLiteRegistration* reg = ops::builtin::Register_ADD();
void* builtin_data_1 = malloc(sizeof(int));
void* builtin_data_2 = malloc(sizeof(int));
void* builtin_data_3 = malloc(sizeof(int));
interpreter_->AddNodeWithParameters({0, 0}, {2}, nullptr, 0, builtin_data_1,
reg);
interpreter_->AddNodeWithParameters({1, 1}, {3}, nullptr, 0, builtin_data_2,
reg);
interpreter_->AddNodeWithParameters({2, 1}, {4}, nullptr, 0, builtin_data_3,
reg);
}
void BuildAsyncSubgraph() {
interpreter_->ModifyGraphWithDelegate(backend_->get_delegate());
subgraph_ = std::make_unique<AsyncSubgraph>(interpreter_->subgraph(0));
}
void TearDown() override { subgraph_.reset(); }
protected:
std::unique_ptr<testing::MockAsyncKernel> kernel_;
std::unique_ptr<testing::TestBackend> backend_;
std::unique_ptr<Interpreter> interpreter_;
std::unique_ptr<AsyncSubgraph> subgraph_;
};
TEST_F(AsyncSubgraphTest, FullyDelegated) {
BuildAsyncSubgraph();
EXPECT_TRUE(AsyncSubgraphTestPeer(subgraph_.get()).IsFullyDelegated());
}
TEST_F(AsyncSubgraphTest, NotFullyDelegated) {
// Don't do delegation.
backend_->SetMinPartitionedNodes(42);
BuildAsyncSubgraph();
EXPECT_FALSE(AsyncSubgraphTestPeer(subgraph_.get()).IsFullyDelegated());
}
TEST_F(AsyncSubgraphTest, BasicTest) {
BuildAsyncSubgraph();
EXPECT_CALL(*kernel_, RegisterBuffer(_, _, _, _, _));
EXPECT_CALL(*kernel_, RegisterBufferSlice(_, _, _, _));
EXPECT_CALL(*kernel_, UnregisterBuffer(_, _));
EXPECT_CALL(*kernel_, ReconcileRestrictions(_, _, _, _, _, _));
EXPECT_CALL(*kernel_, SetAttributes(_, _, _, _));
EXPECT_CALL(*kernel_, Prepare(_, _));
EXPECT_CALL(*kernel_, Eval(_, _, _));
EXPECT_CALL(*kernel_, Wait(_, _));
EXPECT_CALL(*kernel_, Finish(_, _));
auto* buffer = TfLiteBackendBufferCreate();
auto* attrs = new TfLiteAttributeMap(kTfLiteAttrMapTypeBuffer);
TfLiteBufferHandle handle = 1;
TfLiteBufferHandle another_handle = 1;
auto* task = new TfLiteExecutionTask;
EXPECT_FALSE(task->task->Scheduled());
subgraph_->RegisterBuffer(kTfLiteIoTypeInput, buffer, attrs, &handle);
subgraph_->RegisterBufferSlice(handle, attrs, &another_handle);
subgraph_->UnregisterBuffer(handle);
subgraph_->ReconcileRestrictions(0, attrs, attrs, attrs);
subgraph_->SetAttributes(0, attrs);
subgraph_->Prepare();
EXPECT_EQ(kTfLiteOk, subgraph_->InvokeAsync(task));
EXPECT_TRUE(task->task->Scheduled());
// Scheduling another execution w/o waiting on the task should return error.
EXPECT_EQ(kTfLiteError, subgraph_->InvokeAsync(task));
EXPECT_TRUE(task->task->Scheduled());
EXPECT_EQ(kTfLiteOk, task->task->Status());
EXPECT_EQ(kTfLiteOk, subgraph_->Wait(task));
// If waiting the task failed, all successive `Wait` should also fail.
task->task->SetStatus(kTfLiteError);
EXPECT_EQ(kTfLiteError, subgraph_->Wait(task));
EXPECT_EQ(kTfLiteError, subgraph_->Wait(task));
EXPECT_FALSE(task->task->Scheduled());
// Deletes `task`
subgraph_->Finish(task);
TfLiteBackendBufferDelete(buffer);
delete attrs;
EXPECT_NE(handle, another_handle);
}
TEST_F(AsyncSubgraphTest, OutOfBoundTest) {
BuildAsyncSubgraph();
auto* attrs = new TfLiteAttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_FALSE(subgraph_->ReconcileRestrictions(42, attrs, attrs, attrs));
EXPECT_EQ(kTfLiteError, subgraph_->SetAttributes(42, attrs));
delete attrs;
}
} // namespace async
} // namespace tflite
@@ -0,0 +1,21 @@
/* 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_CORE_ASYNC_BACKEND_ASYNC_KERNEL_INTERFACE_H_
#define TENSORFLOW_LITE_CORE_ASYNC_BACKEND_ASYNC_KERNEL_INTERFACE_H_
#include "tensorflow/lite/async/backend_async_kernel_interface.h" // IWYU pragma: export
// IWYU pragma: private, include "third_party/tensorflow/lite/async/backend_async_kernel_interface.h"
#endif // TENSORFLOW_LITE_CORE_ASYNC_BACKEND_ASYNC_KERNEL_INTERFACE_H_
+156
View File
@@ -0,0 +1,156 @@
# This package contains the C API libraries for asynchronous execution and buffer interop.
# For clients using async APIs, please use tensorflow/lite/async/c instead of this package.
# NOTE: Targets in this package are experimental.
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load(
"//tensorflow/lite:build_def.bzl",
"tflite_cc_library_with_c_headers_test",
"tflite_copts",
"tflite_copts_warnings",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
licenses = ["notice"],
)
exports_files(
srcs = [
"types.h",
],
visibility = [
"//tensorflow/lite:__subpackages__",
],
)
tflite_cc_library_with_c_headers_test(
name = "types",
hdrs = ["types.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
# TODO(b/271281434): Update targets that depend on this to instead use
# tensorflow/lite/async/c.
"//visibility:public",
],
)
tflite_cc_library_with_c_headers_test(
name = "async_kernel",
srcs = ["async_kernel.cc"],
hdrs = ["async_kernel.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
# TODO(b/271281434): Update targets that depend on this to instead use
# tensorflow/lite/async/c.
"//visibility:public",
],
deps = [
":types",
"//tensorflow/lite/core/async:async_kernel_internal",
"//tensorflow/lite/core/async/interop/c:attribute_map",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
tflite_cc_library_with_c_headers_test(
name = "task",
srcs = ["task.cc"],
hdrs = ["task.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
# TODO(b/271281434): Update targets that depend on this to instead use
# tensorflow/lite/async/c.
"//visibility:public",
],
deps = [
":types",
"//tensorflow/lite/core/async:task_internal",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
cc_test(
name = "task_test",
srcs = ["task_test.cc"],
deps = [
":task",
":types",
"//tensorflow/lite/core/async:task_internal",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:common",
"@com_google_googletest//:gtest_main",
],
)
tflite_cc_library_with_c_headers_test(
name = "async_signature_runner",
srcs = ["async_signature_runner.cc"],
hdrs = ["async_signature_runner.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = [
# TODO(b/271281434): Update targets that depend on this to instead use
# tensorflow/lite/async/c.
"//visibility:public",
],
deps = [
":internal",
":types",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/core/async:async_signature_runner",
"//tensorflow/lite/core/async/interop/c:attribute_map",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/c:c_api",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
cc_test(
name = "async_signature_runner_test",
srcs = ["async_signature_runner_test.cc"],
copts = tflite_copts() + tflite_copts_warnings(),
data = [
"//tensorflow/lite:testdata/no_signatures.bin",
],
deps = [
":async_signature_runner",
":internal",
":task",
":types",
"//tensorflow/lite:interpreter_test_util",
"//tensorflow/lite/c:c_api_experimental",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/core:cc_api_stable",
"//tensorflow/lite/core/async:async_kernel_internal",
"//tensorflow/lite/core/async:backend_async_kernel_interface",
"//tensorflow/lite/core/async/interop/c:attribute_map",
"//tensorflow/lite/core/async/interop/c:types",
"//tensorflow/lite/core/async/testing:mock_async_kernel",
"//tensorflow/lite/core/async/testing:test_backend",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:c_api_without_op_resolver",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/core/kernels:builtin_ops",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "internal",
hdrs = ["internal.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts() + tflite_copts_warnings(),
visibility = ["//visibility:private"],
deps = ["//tensorflow/lite/core/async:async_signature_runner"],
)
@@ -0,0 +1,161 @@
/* Copyright 2023 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/core/async/c/async_kernel.h"
#include <cstddef>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
TfLiteAsyncKernel* TfLiteAsyncKernelCreate(void* kernel_data) {
TfLiteAsyncKernel* ret = new TfLiteAsyncKernel{};
if (!ret) return nullptr;
ret->kernel_data = kernel_data;
return ret;
}
void* TfLiteAsyncKernelGetKernelData(const TfLiteAsyncKernel* async_kernel) {
if (!async_kernel) return nullptr;
return async_kernel->kernel_data;
}
void TfLiteAsyncKernelSetRegisterBuffer(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*register_buffer)(
TfLiteAsyncKernel* async_kernel, TfLiteOpaqueContext* context,
TfLiteIoType io_type, const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs, TfLiteBufferHandle handle)) {
if (!async_kernel) return;
async_kernel->register_buffer = register_buffer;
}
void TfLiteAsyncKernelSetRegisterBufferSlice(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*register_buffer_slice)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle handle)) {
if (!async_kernel) return;
async_kernel->register_buffer_slice = register_buffer_slice;
}
void TfLiteAsyncKernelSetUnregisterBuffer(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*unregister_buffer)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle handle)) {
if (!async_kernel) return;
async_kernel->unregister_buffer = unregister_buffer;
}
void TfLiteAsyncKernelSetSupportedBufferTypes(
TfLiteAsyncKernel* async_kernel,
void (*supported_buffer_types)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types,
size_t* n_types)) {
if (!async_kernel) return;
async_kernel->supported_buffer_types = supported_buffer_types;
}
void TfLiteAsyncKernelSetSupportedSynchronizations(
TfLiteAsyncKernel* async_kernel,
void (*supported_synchronizations)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types,
size_t* n_types)) {
if (!async_kernel) return;
async_kernel->supported_synchronizations = supported_synchronizations;
}
void TfLiteAsyncKernelSetReconcileRestrictions(
TfLiteAsyncKernel* async_kernel,
bool (*reconcile_restrictions)(
const TfLiteAsyncKernel* async_kernel,
const TfLiteOpaqueContext* context, const TfLiteOpaqueNode* node,
int tensor_index, const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict)) {
if (!async_kernel) return;
async_kernel->reconcile_restrictions = reconcile_restrictions;
}
void TfLiteAsyncKernelSetSetAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*set_attributes)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node, int tensor_index,
const TfLiteAttributeMap* attrs)) {
if (!async_kernel) return;
async_kernel->set_attributes = set_attributes;
}
void TfLiteAsyncKernelSetSetBufferAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*set_buffer_attributes)(TfLiteAsyncKernel* async_kernel,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs)) {
if (!async_kernel) return;
async_kernel->set_buffer_attributes = set_buffer_attributes;
}
void TfLiteAsyncKernelSetGetBufferAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*get_buffer_attributes)(TfLiteAsyncKernel* async_kernel,
const TfLiteBackendBuffer* buffer,
TfLiteAttributeMap* attrs)) {
if (!async_kernel) return;
async_kernel->get_buffer_attributes = get_buffer_attributes;
};
void TfLiteAsyncKernelSetPrepare(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*prepare)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node)) {
if (!async_kernel) return;
async_kernel->prepare = prepare;
}
void TfLiteAsyncKernelSetEval(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*eval)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context, TfLiteOpaqueNode* node,
TfLiteExecutionTask* task)) {
if (!async_kernel) return;
async_kernel->eval = eval;
}
void TfLiteAsyncKernelSetWait(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*wait)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task)) {
if (!async_kernel) return;
async_kernel->wait = wait;
}
void TfLiteAsyncKernelSetFinish(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*finish)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task)) {
if (!async_kernel) return;
async_kernel->finish = finish;
}
void TfLiteAsyncKernelDelete(TfLiteAsyncKernel* async_kernel) {
delete async_kernel;
}
+304
View File
@@ -0,0 +1,304 @@
/* Copyright 2023 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_ASYNC_C_ASYNC_KERNEL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_C_ASYNC_KERNEL_H_
// TODO(b/270731824): Add full documentation / tests for this header.
// Please reference to tensorflow/lite/core/async/async_kernel_internal.h
// for documentation.
#include <stdbool.h>
#include <stddef.h>
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/attribute_map.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/// APIs for asynchronous delegate kernel.
///
/// WARNING: This is an experimental API and subject to change.
/// Opaque TfLiteAsyncKernel type.
typedef struct TfLiteAsyncKernel TfLiteAsyncKernel;
/// Creates an async kernel to be initialized.
/// `kernel_data` is the arbitrary data for identifying the async kernel itself
/// and can be retrieved using `TfLiteAsyncKernelGetKernelData`.
/// NOTE: TfLiteAsyncKernel does not own `kernel_data` and callers should
/// ensure `kernel_data` out-lives the returned `TfLiteAsyncKernel`.
TFL_CAPI_EXPORT extern TfLiteAsyncKernel* TfLiteAsyncKernelCreate(
void* kernel_data);
/// Retrieves the kernel data for identifying the async kernel itself.
TFL_CAPI_EXPORT extern void* TfLiteAsyncKernelGetKernelData(
const TfLiteAsyncKernel* async_kernel);
/// Buffer operations
/// ======================
/// Sets the callback for registering a piece of platform-specific hardware
/// buffer object.
/// `kernel_data` will be the same value supplied by `TfLiteAsyncKernelCreate`.
///
/// `register_buffer`:
/// Registers the buffer to `handle`.
/// `buffer` and `attrs` lifespan is not guaranteed after the function call
/// returns.
/// kernels should save the stored attributes instead of caching the
/// attribute map object itself.
/// `io_type` specifies whether this buffer is used as an input buffer
/// or an output buffer.
/// `attrs` describes the attributes of the buffer object. It's guaranteed to be
/// of kTfLiteBufferAttrMap type and not null. The application must provide
/// `kTfLiteBufferAttrKeyResourceTypeName` attribute. When additional attributes
/// (e.g. padding, size) are provided, the backend is responsible for validating
/// those attributes to be compatible.
/// Once its registered, TfLite runtime will assign and populate `handle` as
/// the buffer handle.
/// The backend will not own the actual buffer object, but the
/// backend can choose to increase the ref count if underlying implementation
/// supports that.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetRegisterBuffer(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*register_buffer)(
TfLiteAsyncKernel* async_kernel, TfLiteOpaqueContext* context,
TfLiteIoType io_type, const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs, TfLiteBufferHandle handle));
/// Sets the callback for registering a buffer slice from previously registered
/// hardware buffer object.
///
/// `register_buffer_slice`:
/// Registers a buffer slice from a previously registered buffer object.
/// `buffer_pool` is the handle of the buffer pool previously registered.
/// `attrs` contains the information of the buffer slice.
/// Once its registered, TfLite runtime will assign and populate `handle` as
/// the buffer handle.
/// NOTE: The backend is responsible to validate the slicing is "valid":
/// * The slicing is not nested from another slice. (i.e. the `buffer_pool` is
/// a handle returned by `RegisterBuffer`.)
/// * The attributes of the slice (e.g. size, offset) is of valid values
/// from the buffer pool.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetRegisterBufferSlice(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*register_buffer_slice)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle buffer_pool,
const TfLiteAttributeMap* attrs,
TfLiteBufferHandle handle));
/// Sets the callback for unregistering a buffer handle.
///
/// `unregister_buffer`:
/// Unregisters a buffer or a buffer slice.
/// `handle` is a buffer handle previously assigned via register_* calls.
/// If the `handle` is not recognized, returns error.
/// NOTE: Unregistering the buffer does not mean deallocating the buffer object.
/// But the backend need to reduce the ref-count if ref counting is performed
/// during buffer registration calls.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetUnregisterBuffer(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*unregister_buffer)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteBufferHandle handle));
/// Reconciliation methods
/// =============================
/// Sets the callback for the backend reporting supported hardware buffer object
/// type names.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetSupportedBufferTypes(
TfLiteAsyncKernel* async_kernel,
void (*supported_buffer_types)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types, size_t* n_types));
/// Sets the callback for the backend reporting supported synchronization object
/// type names.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetSupportedSynchronizations(
TfLiteAsyncKernel* async_kernel,
void (*supported_synchronizations)(const TfLiteAsyncKernel* async_kernel,
TfLiteIoType io_type,
const char* const** types,
size_t* n_types));
/// Sets the callback for the backend to reconcile execution environment
/// attributes (e.g. buffer / synchronization object properties).
///
/// `reconcile_restrictions`:
/// Reconciles buffer or sync attributes for tensor at `tensor_index`.
/// Fills `merged` with reconciled attributes.
/// If `conflict` is provided, conflicting attributes should be provided there.
/// If the type of the `user_provided_attributes` is not recognizable, returns
/// error.
/// If any of the attribute in the `user_provided_attributes` is not
/// recognizable skip this attribute.
/// Returns true if the attribute map type is recognizable and there's no
/// conflicting attribute.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetReconcileRestrictions(
TfLiteAsyncKernel* async_kernel,
bool (*reconcile_restrictions)(
const TfLiteAsyncKernel* async_kernel,
const TfLiteOpaqueContext* context, const TfLiteOpaqueNode* node,
int tensor_index, const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict));
/// Sets the callback for the backend to set buffer / synchronization
/// attributes.
///
/// `set_attributes`:
/// Sets the input / output buffer / synchronization object attributes.
/// Backend kernel will check the attributes covers all the requirements.
/// A typical workflow is for callers call Reconcile*Restrictions method
/// above to have a merged attribute list, check all restrictions are met
/// and set input / output attribute here.
/// Returns kTfLiteOk if provided `attrs` covers all requirements.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetSetAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*set_attributes)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node, int tensor_index,
const TfLiteAttributeMap* attrs));
/// Sets the callback for the backend to set buffer attributes.
///
/// `set_buffer_attributes`:
/// Sets the attributes of the buffers.
/// Backend kernel will check if the provided buffer has been registered, and
/// update the map in the backend, so that the callers can retrieve specific
/// buffer's attributes. `attrs` should be initialized
/// before calling this function and could be constructed by calling
/// TfLiteAttributeMapCreate(). The attributes will be sent to backend kernels
/// and stored in the map with the buffer. `buffer` and `attrs` should not be
/// nullptr. The buffer needs to be registered before calling this
/// function. Returns kTfLiteOk if the buffer has been registered and
/// callers can successfully set the attributes for a buffer.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetSetBufferAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*set_buffer_attributes)(TfLiteAsyncKernel* async_kernel,
const TfLiteBackendBuffer* buffer,
const TfLiteAttributeMap* attrs));
/// Sets the callback for the backend to get buffer attributes.
///
/// `get_buffer_attributes`:
/// Gets the attributes of the buffers.
/// Backend kernel will check if the provided buffer has been registered, and
/// get the corresponding attributes from the map. `attrs` should be initialized
/// before calling this function and could be constructed by calling
/// TfLiteAttributeMapCreate(). `attrs` will be used to store the attributes
/// obtained from the backend kernel. If `attrs` is a non-empty map, it will be
/// overwritten by the attributes of the buffer. `buffer` and `attrs` should not
/// be nullptr. The buffer needs to be registered before calling this function.
/// Returns kTfLiteOk if the buffer has been registered and callers can
/// successfully get the attributes for a buffer.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetGetBufferAttributes(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*get_buffer_attributes)(TfLiteAsyncKernel* async_kernel,
const TfLiteBackendBuffer* buffer,
TfLiteAttributeMap* attrs));
/// Sets the callback to prepare the kernels using the information from
/// `set_attributes` calls.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetPrepare(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*prepare)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteOpaqueNode* node));
/// Execution methods
/// =============================
/// Sets the callback for the backend to schedule an execution.
///
/// `eval`:
/// Schedules an execution with the information provided in task.
/// The application is responsible for filling out buffer and sync mappings
/// to tensors.
/// Backend will set the sync ptr for related tensors if requested.
/// i.e. SetOutputAttributes has sync implementation requested, and
/// the TfLiteSynchronization is not null for the tensor in `task`.
///
/// TfLite runtime guarantees that the task is in ready state (i.e. no
/// un-ended execution for this task).
///
/// Input synchronizations:
/// If the synchronization of a input tensor is `kTfLiteSyncTypeNoSyncObj`
/// type or it's nullptr, it means the data is ready during Eval call.
/// If not, data will be available when the synchronization signals and the
/// backend is responsible for closing the underlying synchronization.
/// The backend is responsible for dedupping the input sync.
///
/// Output synchronizations:
/// If the synchronization type is `kTfLiteSyncTypeNoSyncObj` or is nullptr,
/// the backend does not need to provide synchronization objects to the user.
/// Otherwise, the backend need to provide the sync according to the sync type
/// provided. The underlying sync object will be closed by the app (or
/// downstream components).
/// If there are multiple non-nullptr kTfLiteSynchronization provided for
/// different output tensors, the backend is responsible for duplicating the
/// synchronization.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetEval(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*eval)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context, TfLiteOpaqueNode* node,
TfLiteExecutionTask* task));
/// Sets the callback for the backend to wait for a specific execution.
///
/// `wait`:
/// Waits on the execution scheduled using the task to finish.
/// TfLite runtime guarantees that the task has an un-ended execution.
/// Callers should be able to call `Wait` on the same task from multiple
/// threads, and those calls should return the same status (i.e. if the backend
/// failed to successfully wait on the task, all `Wait` to the task should
/// return the same error before a new invocation is scheduled). Returns
/// kTfLiteOk if the task is finished (w/ or w/o blocking).
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetWait(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*wait)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task));
/// Sets the callback for the backend to finish an execution and release all
/// intermediate resources.
///
/// `finish`:
/// Finishes the task and clean up allocated resources for the task.
/// May block if there's pending executions.
/// This function will be called once and only once for individual task.
/// Returns kTfLiteOk if there's no error. The backend is responsible to
/// clean up task resources regardless there's error or not.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelSetFinish(
TfLiteAsyncKernel* async_kernel,
TfLiteStatus (*finish)(TfLiteAsyncKernel* async_kernel,
TfLiteOpaqueContext* context,
TfLiteExecutionTask* task));
/// Releases `kernel`.
/// Does not release `kernel_data`.
TFL_CAPI_EXPORT extern void TfLiteAsyncKernelDelete(TfLiteAsyncKernel* kernel);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_C_ASYNC_KERNEL_H_
@@ -0,0 +1,230 @@
/* Copyright 2023 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/core/async/c/async_signature_runner.h"
#include <cstddef>
#include <cstdint>
#include "tensorflow/lite/c/c_api_internal.h"
#include "tensorflow/lite/core/async/async_signature_runner.h"
#include "tensorflow/lite/core/async/c/internal.h"
#include "tensorflow/lite/core/c/c_api_types.h"
TfLiteAsyncSignatureRunner* TfLiteInterpreterGetAsyncSignatureRunner(
const TfLiteInterpreter* interpreter, const char* signature_key) {
if (!interpreter) return nullptr;
tflite::async::AsyncSignatureRunner* runner =
interpreter->impl->GetAsyncSignatureRunner(signature_key);
if (!runner) return nullptr;
return new TfLiteAsyncSignatureRunner{runner};
}
TfLiteStatus TfLiteAsyncSignatureRunnerRegisterBuffer(
TfLiteAsyncSignatureRunner* async_signature_runner, TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer, const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->RegisterBuffer(io_type, buffer, attrs,
handle);
}
TfLiteStatus TfLiteAsyncSignatureRunnerRegisterBufferSlice(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteBufferHandle buffer_pool, const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->RegisterBufferSlice(buffer_pool, attrs,
handle);
}
TfLiteStatus TfLiteAsyncSignatureRunnerUnregisterBuffer(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteBufferHandle handle) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->UnregisterBuffer(handle);
}
TfLiteStatus TfLiteAsyncSignatureRunnerGetSupportedBufferTypes(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* const** types, size_t* num_types) {
if (async_signature_runner == nullptr || types == nullptr ||
num_types == nullptr)
return kTfLiteError;
const auto& buffer_types =
async_signature_runner->impl->SupportedBufferTypes(io_type);
*types = buffer_types.data();
*num_types = buffer_types.size();
return kTfLiteOk;
}
TfLiteStatus TfLiteAsyncSignatureRunnerGetSupportedSynchronizationTypes(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* const** types, size_t* num_types) {
if (async_signature_runner == nullptr || types == nullptr ||
num_types == nullptr)
return kTfLiteError;
const auto& synchronization_types =
async_signature_runner->impl->SupportedSynchronizations(io_type);
*types = synchronization_types.data();
*num_types = synchronization_types.size();
return kTfLiteOk;
}
bool TfLiteAsyncSignatureRunnerReconcileRestrictions(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* name,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) {
if (!async_signature_runner) return false;
return async_signature_runner->impl->ReconcileRestrictions(
io_type, name, user_provided_attributes, merged, conflict);
}
bool TfLiteAsyncSignatureRunnerReconcileRestrictionsByIndex(
const TfLiteAsyncSignatureRunner* async_signature_runner, int tensor_index,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict) {
if (!async_signature_runner) return false;
return async_signature_runner->impl->ReconcileRestrictions(
tensor_index, user_provided_attributes, merged, conflict);
}
TfLiteStatus TfLiteAsyncSignatureRunnerSetAttributes(
TfLiteAsyncSignatureRunner* async_signature_runner, TfLiteIoType io_type,
const char* name, const TfLiteAttributeMap* attrs) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->SetAttributes(io_type, name, attrs);
}
TfLiteStatus TfLiteAsyncSignatureRunnerSetAttributesByIndex(
TfLiteAsyncSignatureRunner* async_signature_runner, int tensor_index,
const TfLiteAttributeMap* attrs) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->SetAttributes(tensor_index, attrs);
}
TfLiteStatus TfLiteAsyncSignatureRunnerPrepareBackends(
TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->PrepareBackends();
}
TfLiteExecutionTask* TfLiteAsyncSignatureRunnerCreateTask(
TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->CreateTask();
}
TfLiteStatus TfLiteAsyncSignatureRunnerInvokeAsync(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->InvokeAsync(task);
}
TfLiteStatus TfLiteAsyncSignatureRunnerWait(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->Wait(task);
}
TfLiteStatus TfLiteAsyncSignatureRunnerFinish(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task) {
if (!async_signature_runner) return kTfLiteError;
return async_signature_runner->impl->Finish(task);
}
size_t TfLiteAsyncSignatureRunnerGetInputCount(
const TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return 0;
return async_signature_runner->impl->input_size();
}
const char* TfLiteAsyncSignatureRunnerGetInputName(
const TfLiteAsyncSignatureRunner* async_signature_runner,
int32_t input_index) {
if (!async_signature_runner) return nullptr;
size_t count =
TfLiteAsyncSignatureRunnerGetInputCount(async_signature_runner);
if (input_index < 0 || input_index >= count) {
return nullptr;
}
const auto& input_names = async_signature_runner->impl->input_names();
if (input_index >= input_names.size()) {
return nullptr;
}
return input_names[input_index];
}
size_t TfLiteAsyncSignatureRunnerGetOutputCount(
const TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return 0;
return async_signature_runner->impl->output_size();
}
const char* TfLiteAsyncSignatureRunnerGetOutputName(
const TfLiteAsyncSignatureRunner* async_signature_runner,
int32_t output_index) {
if (!async_signature_runner) return nullptr;
size_t count =
TfLiteAsyncSignatureRunnerGetOutputCount(async_signature_runner);
if (output_index < 0 || output_index >= count) {
return nullptr;
}
const auto& output_names = async_signature_runner->impl->output_names();
if (output_index >= output_names.size()) {
return nullptr;
}
return async_signature_runner->impl->output_names()[output_index];
}
const TfLiteOpaqueTensor* TfLiteAsyncSignatureRunnerGetInputTensor(
TfLiteAsyncSignatureRunner* async_signature_runner,
const char* input_name) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->input_tensor(input_name);
}
const TfLiteOpaqueTensor* TfLiteAsyncSignatureRunnerGetOutputTensor(
const TfLiteAsyncSignatureRunner* async_signature_runner,
const char* output_name) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->output_tensor(output_name);
}
void TfLiteAsyncSignatureRunnerDelete(
TfLiteAsyncSignatureRunner* signature_runner) {
delete signature_runner;
}
const int* TfLiteAsyncSignatureRunnerInputTensorIndices(
const TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->inputs().data();
}
const int* TfLiteAsyncSignatureRunnerOutputTensorIndices(
const TfLiteAsyncSignatureRunner* async_signature_runner) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->outputs().data();
}
const TfLiteOpaqueTensor* TfLiteAsyncSignatureRunnerGetTensor(
const TfLiteAsyncSignatureRunner* async_signature_runner, int index) {
if (!async_signature_runner) return nullptr;
return async_signature_runner->impl->tensor(index);
}
@@ -0,0 +1,340 @@
/* Copyright 2023 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_ASYNC_C_ASYNC_SIGNATURE_RUNNER_H_
#define TENSORFLOW_LITE_CORE_ASYNC_C_ASYNC_SIGNATURE_RUNNER_H_
#include <stdbool.h>
#include <stdint.h>
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/attribute_map.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/// APIs for asynchronous execution using TFLite AsyncSignatureRunner.
///
/// WARNING: This is an experimental API and subject to change.
/// Opaque TfLiteAsyncSignatureRunner type.
typedef struct TfLiteAsyncSignatureRunner TfLiteAsyncSignatureRunner;
/// Returns a new async signature runner using the provided interpreter and
/// signature key, or nullptr on failure.
///
/// NOTE: `signature_key` is a null-terminated C string that must match the
/// key of a signature in the interpreter's model.
///
/// NOTE: The returned signature runner should be destroyed, by calling
/// TfLiteAsyncSignatureRunnerDelete(), before the interpreter is destroyed.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteAsyncSignatureRunner*
TfLiteInterpreterGetAsyncSignatureRunner(const TfLiteInterpreter* interpreter,
const char* signature_key);
/// Registers a TfLiteBackendBuffer to the backend.
/// `async_signature_runner`, `buffer`, `attrs` and `handle` should be non-null.
/// If the hardware buffer wrapped in `buffer` is successfully registered,
/// `handle` will be filled with a new buffer handle. Caller can use the buffer
/// handle as input / output buffer in `TfLiteExecutionTask`.
/// Returns kTfLiteError if the registration failed.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerRegisterBuffer(
TfLiteAsyncSignatureRunner* async_signature_runner, TfLiteIoType io_type,
const TfLiteBackendBuffer* buffer, const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
/// Registers a buffer slice from a previously registered handle `buffer_pool`.
/// `async_signature_runner`, `attrs` and `handle` should be non-null.
/// If the buffer slice described by `attrs` is successfully registered,
/// output `handle` will be filled with a new buffer handle value.
/// NOTE: `attrs` should contain the information about the buffer slice,
/// e.g. offset and size of the size (if applicable).
/// Returns kTfLiteError if the registration failed.
TFL_CAPI_EXPORT extern TfLiteStatus
TfLiteAsyncSignatureRunnerRegisterBufferSlice(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteBufferHandle buffer_pool, const TfLiteAttributeMap* attrs,
TfLiteBufferHandle* handle);
/// Unregisters a hardware buffer object (or buffer slice) with `handle`.
/// Buffer slices should be unregistered before unregistering the buffer pool
/// it belongs to.
/// Returns kTfLiteError if `handle` is not recognized.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerUnregisterBuffer(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteBufferHandle handle);
/// Returns supported platform-specific hardware buffer types.
///
/// Output `types` will be a array of C strings that can be used as the
/// value of `kTfLiteBufferAttrKeyResourceTypeName`.
/// Output `num_types` is the size of the `types` array, and can be used to
/// access elements in `types`.
///
/// NOTE: The lifetime of the returned array is the same as (and depends on) the
/// lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern TfLiteStatus
TfLiteAsyncSignatureRunnerGetSupportedBufferTypes(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* const** types, size_t* num_types);
/// Returns supported platform-specific synchronization object types.
///
/// Output `types` will be a array of C strings that can be used as the
/// value of `kTfLiteSynchronizationAttrKeyObjectTypeName`.
/// Output `num_types` is the size of the `types` array, and can be used to
/// access elements in `types`.
///
/// NOTE: The lifetime of the returned array is the same as (and depends on) the
/// lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern TfLiteStatus
TfLiteAsyncSignatureRunnerGetSupportedSynchronizationTypes(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* const** types, size_t* num_types);
/// Reconciles restrictions with the backend for I/O tensor called `name`.
/// The backend will read `user_provided_attributes` and tries to reconcile
/// those attributes. The backend will also populate its own restrictions
/// back to the caller.
/// The merged attributes will be populated to `merged`. For attributes that
/// the backend does not know or not care about, those will also be copied to
/// `merged` attributes.
/// If there's a conflicting attribute, it will be populated to `conflict` if
/// it's provided.
/// `user_provided_attributes` and `merged` should not be nullptr.
/// Returns true if the reconcilation succeeded and there's no
/// conflicting attributes.
TFL_CAPI_EXPORT extern bool TfLiteAsyncSignatureRunnerReconcileRestrictions(
const TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteIoType io_type, const char* name,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict);
/// Reconciles restrictions with the backend for I/O tensor at `tensor_index`.
/// The backend will read `user_provided_attributes` and tries to reconcile
/// those attributes. The backend will also populate its own restrictions
/// back to the caller.
/// The merged attributes will be populated to `merged`. For attributes that
/// the backend does not know or not care about, those will also be copied to
/// `merged` attributes.
/// If there's a conflicting attribute, it will be populated to `conflict` if
/// it's provided.
/// `user_provided_attributes` and `merged` should not be nullptr.
/// Returns true if the reconcilation succeeded and there's no
/// conflicting attributes.
TFL_CAPI_EXPORT extern bool
TfLiteAsyncSignatureRunnerReconcileRestrictionsByIndex(
const TfLiteAsyncSignatureRunner* async_signature_runner, int tensor_index,
const TfLiteAttributeMap* user_provided_attributes,
TfLiteAttributeMap* merged, TfLiteAttributeMap* conflict);
/// Finalizes I/O tensor `name`'s attributes with `attrs`.
/// The attributes will be forwarded to all backend kernels that depends on
/// tensor. Must call `TfLiteAsyncSignatureRunnerPrepareBackends` after setting
/// new attributes.
/// Callers needs to ensure the lifetime of `name` and `attrs` before this
/// function returns, and those may be deallocated afterwards.
/// Returns true if all backends accept the `attrs`.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerSetAttributes(
TfLiteAsyncSignatureRunner* async_signature_runner, TfLiteIoType io_type,
const char* name, const TfLiteAttributeMap* attrs);
/// Finalizes I/O tensor at `tensor_index`'s attributes with `attrs`.
/// The attributes will be forwarded to all backend kernels that depends on
/// tensor. Must call `TfLiteAsyncSignatureRunnerPrepareBackends` after setting
/// new attributes.
/// Callers needs to ensure the lifetime of `name` and `attrs` before this
/// function returns, and those may be deallocated afterwards.
/// Returns true if all backends accept the `attrs`.
TFL_CAPI_EXPORT extern TfLiteStatus
TfLiteAsyncSignatureRunnerSetAttributesByIndex(
TfLiteAsyncSignatureRunner* async_signature_runner, int tensor_index,
const TfLiteAttributeMap* attrs);
/// Prepares delegate backends for execution.
/// Must be called after `TfLiteAsyncSignatureRunnerSetAttributes` and before
/// `TfLiteAsyncSignatureRunnerCreateTask`.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerPrepareBackends(
TfLiteAsyncSignatureRunner* async_signature_runner);
/// Creates an execution task for this signature.
/// Must be called after `TfLiteAsyncSignatureRunnerPrepareBackends` otherwise
/// returns nullptr.
/// When creating a task, all intermediate resources will be allocated
/// for this task.
/// Caller owns the returned task and must release it by calling
/// `TfLiteAsyncSignatureRunnerFinish`.
/// Returns nullptr if the task allocation failed.
TFL_CAPI_EXPORT extern TfLiteExecutionTask*
TfLiteAsyncSignatureRunnerCreateTask(
TfLiteAsyncSignatureRunner* async_signature_runner);
/// Schedules an asynchronous execution with I/O information
/// provided in `task`.
/// `task` should not be nullptr.
///
/// NOTE: For the same `task`,
/// `Wait` and `InvokeAsync` should be called in pairs, unless `Finish(task)` is
/// called and `task` is freed. The application is responsible
/// to call `Wait` after `InvokeAsync` even if all output tensors are associated
/// with synchronizations.
///
/// Returns kTfLiteError if any backend kernels failed to schedule
/// the execution.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerInvokeAsync(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task);
/// Blocks and wait for execution tied to `task` to finish.
/// `task` should not be nullptr.
/// Can be called from multiple threads. All calls will block until the
/// task finishes execution.
///
/// NOTE: For the same `task`,
/// `Wait` and `InvokeAsync` should be called in pairs, unless `Finish(task)` is
/// called and `task` is freed. The application is responsible
/// to call `Wait` after `InvokeAsync` even if all output tensors are associated
/// with synchronizations.
/// If `TfLiteAsyncSignatureRunnerWait` is called without a matching call to
/// `TfLiteAsyncSignatureRunnerInvokeAsync`, returns the latest status code (by
/// default `kTfLiteOk`).
///
/// Returns kTfLiteError if any backends failed to finish the execution.
/// If the task is currently idle, it will return its latest status code.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerWait(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task);
/// Finishes the task and release all intermediate resources tied to
/// this task. Must be called exactly once for each `task` object.
/// If there's ongoing execution, this will block wait for the execution
/// to finish.
/// `task` should not be nullptr and will be deleted.
/// NOTE: Caller needs to ensure `Finish` is not called concurrently with
/// `InvokeAsync` or `Wait`.
/// Returns kTfLiteError if fails to release the task. The task will be
/// destroyed regardless of error or not.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteAsyncSignatureRunnerFinish(
TfLiteAsyncSignatureRunner* async_signature_runner,
TfLiteExecutionTask* task);
/// Returns the number of input tensors associated with the signature.
TFL_CAPI_EXPORT extern size_t TfLiteAsyncSignatureRunnerGetInputCount(
const TfLiteAsyncSignatureRunner* async_signature_runner);
/// Returns the (null-terminated) name of the Nth input in a signature, where N
/// is specified as `input_index`.
///
/// NOTE: The lifetime of the returned name is the same as (and depends on) the
/// lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const char* TfLiteAsyncSignatureRunnerGetInputName(
const TfLiteAsyncSignatureRunner* async_signature_runner,
int32_t input_index);
/// Returns the number of output tensors associated with the signature.
TFL_CAPI_EXPORT extern size_t TfLiteAsyncSignatureRunnerGetOutputCount(
const TfLiteAsyncSignatureRunner* async_signature_runner);
/// Returns the (null-terminated) name of the Nth output in a signature, where
/// N is specified as `output_index`.
///
/// NOTE: The lifetime of the returned name is the same as (and depends on) the
/// lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const char* TfLiteAsyncSignatureRunnerGetOutputName(
const TfLiteAsyncSignatureRunner* async_signature_runner,
int32_t output_index);
/// Returns the input tensor metadata identified by `input_name` in the given
/// signature.
/// Returns nullptr if the given name is not valid.
///
/// NOTE: For AsyncSignatureRunner, tensor data are not stored within
/// `TfLiteOpaqueTensors` but in platform-specific hardware buffer objects.
/// This method is only used for accessing the metadata like shape and data type
/// of the input tensors.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const TfLiteOpaqueTensor*
TfLiteAsyncSignatureRunnerGetInputTensor(
TfLiteAsyncSignatureRunner* async_signature_runner, const char* input_name);
/// Returns the output tensor metadata identified by `output_name` in the given
/// signature.
/// Returns nullptr if the given name is not valid.
///
/// Note: For AsyncSignatureRunner, tensor data are not stored within
/// `TfLiteOpaqueTensors` but in platform-specific hardware buffer objects.
/// This method is only used for accessing the metadata like shape and data type
/// of the output tensors.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const TfLiteOpaqueTensor*
TfLiteAsyncSignatureRunnerGetOutputTensor(
const TfLiteAsyncSignatureRunner* async_signature_runner,
const char* output_name);
/// Destroys the async signature runner.
TFL_CAPI_EXPORT extern void TfLiteAsyncSignatureRunnerDelete(
TfLiteAsyncSignatureRunner* signature_runner);
/// Returns a pointer to an array of input tensor indices. The length of the
/// array can be obtained via a call to
/// `TfLiteAsyncSignatureRunnerGetInputCount`.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const int* TfLiteAsyncSignatureRunnerInputTensorIndices(
const TfLiteAsyncSignatureRunner* async_signature_runner);
/// Returns a pointer to an array of output tensor indices. The length of the
/// array can be obtained via a call to
/// `TfLiteAsyncSignatureRunnerGetOutputCount`.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const int* TfLiteAsyncSignatureRunnerOutputTensorIndices(
const TfLiteAsyncSignatureRunner* async_signature_runner);
/// Returns the tensor metadata identified by `index` in the given
/// signature.
/// Returns nullptr if the given index is not valid or out of bound.
///
/// NOTE: For AsyncSignatureRunner, tensor data are not stored within
/// `TfLiteOpaqueTensors` but in platform-specific hardware buffer objects.
/// This method is only used for accessing the metadata like shape and data type
/// of the input tensors.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `async_signature_runner`.
TFL_CAPI_EXPORT extern const TfLiteOpaqueTensor*
TfLiteAsyncSignatureRunnerGetTensor(
const TfLiteAsyncSignatureRunner* async_signature_runner, int index);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_C_ASYNC_SIGNATURE_RUNNER_H_
@@ -0,0 +1,325 @@
/* Copyright 2023 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/core/async/c/async_signature_runner.h"
#include <cstddef>
#include <cstdlib>
#include <memory>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "tensorflow/lite/c/c_api_internal.h"
#include "tensorflow/lite/c/c_api_opaque.h"
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/backend_async_kernel_interface.h"
#include "tensorflow/lite/core/async/c/internal.h"
#include "tensorflow/lite/core/async/c/task.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/attribute_map.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/async/testing/mock_async_kernel.h"
#include "tensorflow/lite/core/async/testing/test_backend.h"
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/core/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/interpreter_test_util.h"
using ::testing::_;
using ::testing::Return;
namespace tflite {
namespace async {
class AsyncSignatureRunnerTest : public InterpreterTest,
public ::testing::WithParamInterface<bool> {
protected:
void SetUp() override {
kernel_ =
std::make_unique<::testing::StrictMock<testing::MockAsyncKernel>>();
backend_ = std::make_unique<testing::TestBackend>(kernel_->kernel());
auto interpreter = std::make_unique<Interpreter>();
interpreter->AddTensors(2);
interpreter->SetInputs({0});
interpreter->SetOutputs({1});
TfLiteQuantizationParams quant;
interpreter->SetTensorParametersReadWrite(0, kTfLiteFloat32, "x", {3},
quant);
interpreter->SetTensorParametersReadWrite(1, kTfLiteFloat32, "a", {3},
quant);
TfLiteRegistration* reg = ops::builtin::Register_ADD();
void* builtin_data_1 = malloc(sizeof(int));
interpreter->AddNodeWithParameters({0, 0}, {1}, nullptr, 0, builtin_data_1,
reg);
tflite_interpreter_.impl = std::move(interpreter);
}
void BuildRunner(bool has_signature) {
auto* interpreter = tflite_interpreter_.impl.get();
if (has_signature) {
const char kSignatureKey[] = "serving_default";
BuildSignature(interpreter, kSignatureKey, {{"input", 0}},
{{"output", 1}});
interpreter->ModifyGraphWithDelegate(backend_->get_delegate());
runner_ = TfLiteInterpreterGetAsyncSignatureRunner(&tflite_interpreter_,
kSignatureKey);
} else {
interpreter->ModifyGraphWithDelegate(backend_->get_delegate());
runner_ = TfLiteInterpreterGetAsyncSignatureRunner(&tflite_interpreter_,
nullptr);
}
ASSERT_NE(nullptr, runner_);
}
void TearDown() override { TfLiteAsyncSignatureRunnerDelete(runner_); }
protected:
TfLiteAsyncSignatureRunner* runner_ = nullptr;
std::unique_ptr<::testing::StrictMock<testing::MockAsyncKernel>> kernel_;
std::unique_ptr<testing::TestBackend> backend_;
internal::SignatureDef signature_def_;
TfLiteInterpreter tflite_interpreter_{};
};
INSTANTIATE_TEST_SUITE_P(AsyncSignatureRunnerTest, AsyncSignatureRunnerTest,
::testing::Bool());
TEST_P(AsyncSignatureRunnerTest, RegisterBufferTest) {
BuildRunner(GetParam());
EXPECT_CALL(*kernel_, RegisterBuffer(_, _, _, _, _))
.WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel_, RegisterBufferSlice(_, _, _, _))
.WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel_, UnregisterBuffer(_, _)).WillOnce(Return(kTfLiteOk));
TfLiteBufferHandle handle;
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
auto* buf = TfLiteBackendBufferCreate();
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerRegisterBuffer(
runner_, kTfLiteIoTypeInput, buf, attr, &handle));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerRegisterBufferSlice(
runner_, handle, attr, &handle));
EXPECT_EQ(kTfLiteOk,
TfLiteAsyncSignatureRunnerUnregisterBuffer(runner_, handle));
TfLiteAttributeMapDelete(attr);
TfLiteBackendBufferDelete(buf);
}
TEST_P(AsyncSignatureRunnerTest, SupportedTypesTest) {
BuildRunner(GetParam());
const char* const* buffer_types = nullptr;
size_t num_buffer_types = 0;
EXPECT_EQ(kTfLiteOk,
TfLiteAsyncSignatureRunnerGetSupportedBufferTypes(
runner_, kTfLiteIoTypeInput, &buffer_types, &num_buffer_types));
EXPECT_EQ(1, num_buffer_types);
EXPECT_STREQ("buffer_type", buffer_types[0]);
const char* const* sync_types = nullptr;
size_t num_sync_types = 0;
EXPECT_EQ(kTfLiteOk,
TfLiteAsyncSignatureRunnerGetSupportedSynchronizationTypes(
runner_, kTfLiteIoTypeInput, &sync_types, &num_sync_types));
EXPECT_EQ(1, num_sync_types);
EXPECT_STREQ("sync_type", sync_types[0]);
}
TEST_P(AsyncSignatureRunnerTest, ReconcileTest) {
bool has_signature = GetParam();
BuildRunner(has_signature);
EXPECT_CALL(*kernel_, ReconcileRestrictions(_, _, _, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*kernel_, SetAttributes(_, _, _, _)).WillOnce(Return(kTfLiteOk));
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
if (has_signature) {
EXPECT_TRUE(TfLiteAsyncSignatureRunnerReconcileRestrictions(
runner_, kTfLiteIoTypeInput, "input", attr, attr, nullptr));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerSetAttributes(
runner_, kTfLiteIoTypeInput, "input", attr));
} else {
EXPECT_TRUE(TfLiteAsyncSignatureRunnerReconcileRestrictionsByIndex(
runner_, 0, attr, attr, nullptr));
EXPECT_EQ(kTfLiteOk,
TfLiteAsyncSignatureRunnerSetAttributesByIndex(runner_, 0, attr));
}
TfLiteAttributeMapDelete(attr);
}
TEST_P(AsyncSignatureRunnerTest, ExecutionTest) {
BuildRunner(GetParam());
EXPECT_CALL(*kernel_, Prepare(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel_, Eval(_, _, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel_, Wait(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel_, Finish(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerPrepareBackends(runner_));
auto* task = TfLiteAsyncSignatureRunnerCreateTask(runner_);
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerInvokeAsync(runner_, task));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerWait(runner_, task));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerFinish(runner_, task));
}
TEST_P(AsyncSignatureRunnerTest, InputsTest) {
bool has_signature = GetParam();
BuildRunner(has_signature);
EXPECT_EQ(1, TfLiteAsyncSignatureRunnerGetInputCount(runner_));
if (has_signature) {
EXPECT_STREQ("input", TfLiteAsyncSignatureRunnerGetInputName(runner_, 0));
EXPECT_STREQ(
"x", TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetInputTensor(runner_, "input")));
} else {
EXPECT_STREQ("x", TfLiteAsyncSignatureRunnerGetInputName(runner_, 0));
EXPECT_STREQ("x",
TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetInputTensor(runner_, "x")));
}
}
TEST_P(AsyncSignatureRunnerTest, OutputsTest) {
bool has_signature = GetParam();
BuildRunner(has_signature);
EXPECT_EQ(1, TfLiteAsyncSignatureRunnerGetOutputCount(runner_));
if (has_signature) {
EXPECT_STREQ("output", TfLiteAsyncSignatureRunnerGetOutputName(runner_, 0));
EXPECT_STREQ(
"a", TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetOutputTensor(runner_, "output")));
} else {
EXPECT_STREQ("a", TfLiteAsyncSignatureRunnerGetOutputName(runner_, 0));
EXPECT_STREQ("a",
TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetOutputTensor(runner_, "a")));
}
}
TEST_P(AsyncSignatureRunnerTest, InputByIndexTest) {
BuildRunner(GetParam());
EXPECT_EQ(1, TfLiteAsyncSignatureRunnerGetInputCount(runner_));
auto* indices = TfLiteAsyncSignatureRunnerInputTensorIndices(runner_);
EXPECT_NE(nullptr, indices);
auto indice = indices[0];
EXPECT_STREQ("x", TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetTensor(runner_, indice)));
}
TEST_P(AsyncSignatureRunnerTest, OutputsByIndexTest) {
BuildRunner(GetParam());
EXPECT_EQ(1, TfLiteAsyncSignatureRunnerGetOutputCount(runner_));
auto* indices = TfLiteAsyncSignatureRunnerOutputTensorIndices(runner_);
EXPECT_NE(nullptr, indices);
auto indice = indices[0];
EXPECT_STREQ("a", TfLiteOpaqueTensorName(
TfLiteAsyncSignatureRunnerGetTensor(runner_, indice)));
}
TEST_P(AsyncSignatureRunnerTest, IndexOutOfBound) {
BuildRunner(GetParam());
EXPECT_EQ(nullptr, TfLiteAsyncSignatureRunnerGetTensor(runner_, 42));
}
TEST(AsyncSignatureRunnerTest, TestNoSignatures) {
TfLiteModel* model = TfLiteModelCreateFromFile(
"third_party/tensorflow/lite/testdata/no_signatures.bin");
ASSERT_NE(model, nullptr);
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
ASSERT_NE(options, nullptr);
auto kernel =
std::make_unique<::testing::StrictMock<testing::MockAsyncKernel>>();
auto backend = std::make_unique<testing::TestBackend>(kernel->kernel());
TfLiteInterpreterOptionsAddDelegate(options, backend->get_delegate());
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
ASSERT_NE(interpreter, nullptr);
TfLiteInterpreterOptionsDelete(options);
int nun_signatures = TfLiteInterpreterGetSignatureCount(interpreter);
ASSERT_EQ(nun_signatures, 0);
ASSERT_EQ(TfLiteInterpreterGetAsyncSignatureRunner(interpreter, "foo"),
nullptr);
TfLiteAsyncSignatureRunner* runner =
TfLiteInterpreterGetAsyncSignatureRunner(interpreter, nullptr);
ASSERT_NE(runner, nullptr);
int num_interpreter_inputs =
TfLiteInterpreterGetInputTensorCount(interpreter);
int num_runner_inputs = TfLiteAsyncSignatureRunnerGetInputCount(runner);
ASSERT_EQ(num_runner_inputs, num_interpreter_inputs);
for (int i = 0; i < num_interpreter_inputs; ++i) {
auto* interpreter_input_tensor =
TfLiteInterpreterGetInputTensor(interpreter, i);
ASSERT_NE(interpreter_input_tensor, nullptr);
auto* interpreter_input_name = TfLiteTensorName(interpreter_input_tensor);
ASSERT_NE(interpreter_input_name, nullptr);
auto* runner_input_name = TfLiteAsyncSignatureRunnerGetInputName(runner, i);
ASSERT_NE(runner_input_name, nullptr);
EXPECT_STREQ(runner_input_name, interpreter_input_name);
auto* runner_input_tensor = TfLiteAsyncSignatureRunnerGetInputTensor(
runner, interpreter_input_name);
ASSERT_NE(runner_input_tensor, nullptr);
ASSERT_EQ(runner_input_tensor, reinterpret_cast<const TfLiteOpaqueTensor*>(
interpreter_input_tensor));
}
int num_interpreter_outputs =
TfLiteInterpreterGetOutputTensorCount(interpreter);
int num_runner_outputs = TfLiteAsyncSignatureRunnerGetOutputCount(runner);
ASSERT_EQ(num_runner_outputs, num_interpreter_outputs);
for (int i = 0; i < num_interpreter_outputs; ++i) {
auto* interpreter_output_tensor =
TfLiteInterpreterGetOutputTensor(interpreter, i);
ASSERT_NE(interpreter_output_tensor, nullptr);
auto* interpreter_output_name = TfLiteTensorName(interpreter_output_tensor);
ASSERT_NE(interpreter_output_name, nullptr);
auto* runner_output_name =
TfLiteAsyncSignatureRunnerGetOutputName(runner, i);
ASSERT_NE(runner_output_name, nullptr);
EXPECT_STREQ(runner_output_name, interpreter_output_name);
auto* runner_output_tensor = TfLiteAsyncSignatureRunnerGetOutputTensor(
runner, interpreter_output_name);
ASSERT_NE(runner_output_tensor, nullptr);
ASSERT_EQ(runner_output_tensor, reinterpret_cast<const TfLiteOpaqueTensor*>(
interpreter_output_tensor));
}
EXPECT_CALL(*kernel, Prepare(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel, Eval(_, _, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel, Wait(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_CALL(*kernel, Finish(_, _)).WillOnce(Return(kTfLiteOk));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerPrepareBackends(runner));
auto* task = TfLiteAsyncSignatureRunnerCreateTask(runner);
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerInvokeAsync(runner, task));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerWait(runner, task));
EXPECT_EQ(kTfLiteOk, TfLiteAsyncSignatureRunnerFinish(runner, task));
TfLiteAsyncSignatureRunnerDelete(runner);
TfLiteInterpreterDelete(interpreter);
TfLiteModelDelete(model);
}
} // namespace async
} // namespace tflite
+33
View File
@@ -0,0 +1,33 @@
/* Copyright 2023 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_ASYNC_C_INTERNAL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_C_INTERNAL_H_
#include "tensorflow/lite/core/async/async_signature_runner.h"
// Internal structures and subroutines used by the C API. These are likely to
// change and should not be depended on directly by any C API clients.
//
// NOTE: This header does not follow C conventions and does not define a C API.
// It is effectively an (internal) implementation detail of the C API.
struct TfLiteAsyncSignatureRunner {
// The tflite::async::AsyncSignatureRunner runner object that this points to
// is owned by the interpreter. So this pointer will become invalid when the
// interpreter is destroyed.
tflite::async::AsyncSignatureRunner* impl;
};
#endif // TENSORFLOW_LITE_CORE_ASYNC_C_INTERNAL_H_
+111
View File
@@ -0,0 +1,111 @@
/* 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/core/async/c/task.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/task_internal.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
extern "C" {
TfLiteStatus TfLiteExecutionTaskSetBuffer(TfLiteExecutionTask* task,
TfLiteIoType io_type,
const char* tensor_signature_name,
TfLiteBufferHandle handle) {
if (task == nullptr || task->task == nullptr ||
tensor_signature_name == nullptr)
return kTfLiteError;
return task->task->SetBufferHandle(io_type, tensor_signature_name, handle);
}
TfLiteStatus TfLiteExecutionTaskSetBufferByIndex(TfLiteExecutionTask* task,
int tensor_index,
TfLiteBufferHandle handle) {
if (task == nullptr || task->task == nullptr) return kTfLiteError;
return task->task->SetBufferHandle(tensor_index, handle);
}
TfLiteStatus TfLiteExecutionTaskSetSync(TfLiteExecutionTask* task,
TfLiteIoType io_type,
const char* tensor_signature_name,
TfLiteSynchronization* sync) {
if (task == nullptr || task->task == nullptr ||
tensor_signature_name == nullptr)
return kTfLiteError;
return task->task->SetSynchronization(io_type, tensor_signature_name, sync);
}
TfLiteStatus TfLiteExecutionTaskSetSyncByIndex(TfLiteExecutionTask* task,
int tensor_index,
TfLiteSynchronization* sync) {
if (task == nullptr || task->task == nullptr) return kTfLiteError;
return task->task->SetSynchronization(tensor_index, sync);
}
TfLiteBufferHandle TfLiteExecutionTaskGetBufferByName(
const TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name) {
if (task == nullptr || task->task == nullptr ||
tensor_signature_name == nullptr)
return kTfLiteNullBufferHandle;
return task->task->GetBufferHandle(io_type, tensor_signature_name);
}
TfLiteSynchronization* TfLiteExecutionTaskGetSyncByName(
const TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name) {
if (task == nullptr || task->task == nullptr ||
tensor_signature_name == nullptr)
return nullptr;
return task->task->GetSynchronization(io_type, tensor_signature_name);
}
TfLiteBufferHandle TfLiteExecutionTaskGetBufferByIndex(
const TfLiteExecutionTask* task, int tensor_index) {
if (task == nullptr || task->task == nullptr) return kTfLiteNullBufferHandle;
return task->task->GetBufferHandle(tensor_index);
}
TfLiteSynchronization* TfLiteExecutionTaskGetSyncByIndex(
const TfLiteExecutionTask* task, int tensor_index) {
if (task == nullptr || task->task == nullptr) return nullptr;
return task->task->GetSynchronization(tensor_index);
}
void* TfLiteExecutionTaskGetDelegateExecutionData(
const TfLiteExecutionTask* task, TfLiteAsyncKernel* kernel) {
if (task == nullptr || task->task == nullptr) return nullptr;
return task->task->GetDelegateExecutionData(kernel);
}
void TfLiteExecutionTaskSetDelegateExecutionData(TfLiteExecutionTask* task,
TfLiteAsyncKernel* kernel,
void* data) {
if (task == nullptr || task->task == nullptr) return;
task->task->SetDelegateExecutionData(kernel, data);
}
TfLiteStatus TfLiteExecutionTaskGetStatus(const TfLiteExecutionTask* task) {
if (task == nullptr || task->task == nullptr) return kTfLiteError;
return task->task->Status();
}
void TfLiteExecutionTaskSetStatus(TfLiteExecutionTask* task,
TfLiteStatus status) {
if (task == nullptr || task->task == nullptr) return;
task->task->SetStatus(status);
}
}
+161
View File
@@ -0,0 +1,161 @@
/* 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_CORE_ASYNC_C_TASK_H_
#define TENSORFLOW_LITE_CORE_ASYNC_C_TASK_H_
#include <stdint.h>
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// TfLiteExecutionTask API.
///
/// The opaque TfLiteExecutionTask stores the information for a specific
/// execution. It includes the mapping from tensors to the buffer handles as
/// well as the synchronization objects.
/// WARNING: This file contains experimental APIs and subject to change.
/// Opaque type for execution task.
/// NOTE: Unless documented, `TfLiteExecutionTask` objects are
/// "thread-compatible": i.e. not thread-safe but also not thread-hostile
/// <https://web.archive.org/web/20210125044505/https://www.ibm.com/developerworks/java/library/j-jtp09263/index.html>.
/// That is, each instance is not thread-safe, but multiple separate instances
/// are safely independent.
typedef struct TfLiteExecutionTask TfLiteExecutionTask;
/// Buffers
/// --------------------------------------------------------------------------
/// If no synchronization type is set, the input data is default to synchronized
/// (i.e. ready when calling InvokeAsync)
/// Sets the buffer handle to the input / output tensor associated with
/// `tensor_signature_name`.
/// `task` and `tensor_signature_name` must not be nullptr.
/// Returns kTfLiteError if the tensor is not found or nullptr args.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteExecutionTaskSetBuffer(
TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name, TfLiteBufferHandle handle);
/// Sets the buffer handle to the input / output tensor associated with the
/// tensor index.
/// NOTE: This method does not check tensor index is pointing to a valid tensor.
/// Caller need to make sure the tensor_index points to a valid tensor by
/// using the element from AsyncSignatureRunner inputs / outputs array.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteExecutionTaskSetBufferByIndex(
TfLiteExecutionTask* task, int tensor_index, TfLiteBufferHandle handle);
/// Returns the buffer handle of the input / output tensor associated with
/// `tensor_signature_name`.
/// `task` and `tensor_signature_name` must not be nullptr.
/// Returns kTfLiteNullBufferHandle if the tensor is not found or null input.
TFL_CAPI_EXPORT extern TfLiteBufferHandle TfLiteExecutionTaskGetBufferByName(
const TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name);
/// The same as `TfLiteExecutionTaskGetBufferByName` but takes tensor index
/// instead of the name from signature.
TFL_CAPI_EXPORT extern TfLiteBufferHandle TfLiteExecutionTaskGetBufferByIndex(
const TfLiteExecutionTask* task, int tensor_index);
/// Synchronizations
/// --------------------------------------------------------------------------
/// Associates synchronization objects to input / output tensors.
///
/// For input tensor, either a nullptr or default sync type
/// `kTfLiteSyncTypeNoSyncObj` means the input is already ready when scheduling
/// the execution. otherwise, the input data will be ready when the underlying
/// sync object signals. The backend is responsible to close the underlying
/// sync object.
/// For output tensor, if the user does not require the backend to return
/// the sync object, it can set the sync type to default
/// `kTfLiteSyncTypeNoSyncObj` or a nullptr TfLiteSynchronization. It means the
/// data is ready when the application calls `Wait` on the given task. Otherwise
/// the backend needs to provide a not-null sync object according to the sync
/// type and it will be signaled when the output data is ready. The underlying
/// output sync object needs to be closed by the application (or some downstream
/// in the pipeline). The backend will be responsible for duplicating the synch
/// if TfLiteSynchronizations are not nullptr for different output tensor
/// produced by the same backend.
///
/// The application needs to maintain the lifetime of the input
/// TfLiteSynchronizations associated with the task during its invocation.
/// TODO(b/191883048): Revisit if we want to bundle the lifetime of sync with
/// the task itself and delete the TfLiteSynchronization in `Finish(task)`.
/// Sets the opaque sync object to the input / output tensor associated with
/// `tensor_signature_name`.
/// `task` and `tensor_signature_name` must not be nullptr.
/// A nullptr `sync` esentially means the tensor data does not need
/// synchronization.
/// `task` does not take the ownership of `sync`, so caller needs to release
/// `sync` when destroying the `task` with AsyncSignatureRunner::Finish.
/// Returns kTfLiteError if the tensor is not found.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteExecutionTaskSetSync(
TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name, TfLiteSynchronization* sync);
/// Sets the opaque sync object to the input / output tensor associated with the
/// tensor index.
/// NOTE: This method does not check tensor index is pointing to a
/// valid tensor. Caller need to make sure the tensor_index points to a valid
/// tensor by using the element from AsyncSignatureRunner inputs / outputs
/// array.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteExecutionTaskSetSyncByIndex(
TfLiteExecutionTask* task, int tensor_index, TfLiteSynchronization* sync);
/// Returns the sync object of the input / output tensor associated with
/// `tensor_signature_name`.
/// `task` and `tensor_signature_name` must not be nullptr.
/// Returns nullptr if the tensor is not found or null input.
TFL_CAPI_EXPORT extern TfLiteSynchronization* TfLiteExecutionTaskGetSyncByName(
const TfLiteExecutionTask* task, TfLiteIoType io_type,
const char* tensor_signature_name);
/// The same as `TfLiteExecutionTaskGetSyncByName` but takes tensor index
/// instead of the name from signature.
TFL_CAPI_EXPORT extern TfLiteSynchronization* TfLiteExecutionTaskGetSyncByIndex(
const TfLiteExecutionTask* task, int tensor_index);
/// Task execution data
/// Backends may store task specific data for executions. This ease the burden
/// for backends to maintain the mapping across different tasks.
TFL_CAPI_EXPORT extern void* TfLiteExecutionTaskGetDelegateExecutionData(
const TfLiteExecutionTask* task, TfLiteAsyncKernel* kernel);
TFL_CAPI_EXPORT extern void TfLiteExecutionTaskSetDelegateExecutionData(
TfLiteExecutionTask* task, TfLiteAsyncKernel* kernel, void* data);
/// Task status
/// Thread safe accessors for the latest status of the task.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteExecutionTaskGetStatus(
const TfLiteExecutionTask* task);
TFL_CAPI_EXPORT extern void TfLiteExecutionTaskSetStatus(
TfLiteExecutionTask* task, TfLiteStatus status);
// TODO(b/262574034): Also add APIs for error code and error messages.
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_C_TASK_H_
+118
View File
@@ -0,0 +1,118 @@
/* 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/core/async/c/task.h"
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/async/task_internal.h"
#include "tensorflow/lite/core/c/common.h"
namespace {
class TfLiteExecutionTaskTest : public ::testing::Test {
protected:
void SetUp() override {
input_names_["x"] = 1;
input_names_["y"] = 2;
output_names_["a"] = 3;
task_.task->SetInputNameMap(&input_names_);
task_.task->SetOutputNameMap(&output_names_);
}
TfLiteExecutionTask* task() { return &task_; }
protected:
tflite::async::ExecutionTask::TensorNameMapT input_names_;
tflite::async::ExecutionTask::TensorNameMapT output_names_;
TfLiteExecutionTask task_;
};
TEST_F(TfLiteExecutionTaskTest, BasicTest) {
auto* sync = TfLiteSynchronizationCreate();
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeInput, "x", 42));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeInput, "y", 43));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeOutput, "a", 44));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetSync(task(), kTfLiteIoTypeInput, "x", sync));
EXPECT_EQ(
42, TfLiteExecutionTaskGetBufferByName(task(), kTfLiteIoTypeInput, "x"));
EXPECT_EQ(
43, TfLiteExecutionTaskGetBufferByName(task(), kTfLiteIoTypeInput, "y"));
EXPECT_EQ(
44, TfLiteExecutionTaskGetBufferByName(task(), kTfLiteIoTypeOutput, "a"));
EXPECT_EQ(sync,
TfLiteExecutionTaskGetSyncByName(task(), kTfLiteIoTypeInput, "x"));
EXPECT_EQ(nullptr,
TfLiteExecutionTaskGetSyncByName(task(), kTfLiteIoTypeInput, "y"));
EXPECT_EQ(nullptr,
TfLiteExecutionTaskGetSyncByName(task(), kTfLiteIoTypeOutput, "a"));
TfLiteSynchronizationDelete(sync);
}
TEST_F(TfLiteExecutionTaskTest, BasicTestByTensorIndex) {
auto* sync = TfLiteSynchronizationCreate();
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeInput, "x", 42));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeInput, "y", 43));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetBuffer(task(), kTfLiteIoTypeOutput, "a", 44));
EXPECT_EQ(kTfLiteOk,
TfLiteExecutionTaskSetSync(task(), kTfLiteIoTypeInput, "x", sync));
EXPECT_EQ(42, TfLiteExecutionTaskGetBufferByIndex(task(), 1));
EXPECT_EQ(43, TfLiteExecutionTaskGetBufferByIndex(task(), 2));
EXPECT_EQ(44, TfLiteExecutionTaskGetBufferByIndex(task(), 3));
EXPECT_EQ(sync, TfLiteExecutionTaskGetSyncByIndex(task(), 1));
EXPECT_EQ(nullptr, TfLiteExecutionTaskGetSyncByIndex(task(), 2));
EXPECT_EQ(nullptr, TfLiteExecutionTaskGetSyncByIndex(task(), 3));
TfLiteSynchronizationDelete(sync);
}
TEST_F(TfLiteExecutionTaskTest, NullTest) {
EXPECT_EQ(kTfLiteError,
TfLiteExecutionTaskSetBuffer(nullptr, kTfLiteIoTypeInput, "x", 42));
EXPECT_EQ(kTfLiteError, TfLiteExecutionTaskSetSync(
nullptr, kTfLiteIoTypeInput, "x", nullptr));
EXPECT_EQ(kTfLiteNullBufferHandle, TfLiteExecutionTaskGetBufferByName(
nullptr, kTfLiteIoTypeOutput, "a"));
EXPECT_EQ(nullptr,
TfLiteExecutionTaskGetSyncByName(nullptr, kTfLiteIoTypeInput, "x"));
EXPECT_EQ(kTfLiteNullBufferHandle,
TfLiteExecutionTaskGetBufferByIndex(nullptr, 3));
EXPECT_EQ(nullptr, TfLiteExecutionTaskGetSyncByIndex(nullptr, 3));
EXPECT_EQ(kTfLiteError, TfLiteExecutionTaskGetStatus(nullptr));
TfLiteExecutionTaskSetStatus(nullptr, kTfLiteOk);
EXPECT_EQ(kTfLiteError, TfLiteExecutionTaskSetBufferByIndex(nullptr, 0, 0));
EXPECT_EQ(kTfLiteError,
TfLiteExecutionTaskSetSyncByIndex(nullptr, 0, nullptr));
}
TEST_F(TfLiteExecutionTaskTest, StatusTest) {
EXPECT_EQ(kTfLiteOk, TfLiteExecutionTaskGetStatus(task()));
TfLiteExecutionTaskSetStatus(task(), kTfLiteError);
EXPECT_EQ(kTfLiteError, TfLiteExecutionTaskGetStatus(task()));
}
} // namespace
+43
View File
@@ -0,0 +1,43 @@
/* 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_CORE_ASYNC_C_TYPES_H_
#define TENSORFLOW_LITE_CORE_ASYNC_C_TYPES_H_
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/// Opaque type for TfLiteAsyncKernel.
typedef struct TfLiteAsyncKernel TfLiteAsyncKernel;
/// Opaque type for TfLiteExecutionTask.
///
/// See tensorflow/lite/core/async/c/task.h
/// NOTE: TfLiteExecutionTask is NOT thread-safe.
typedef struct TfLiteExecutionTask TfLiteExecutionTask;
/// Enum tag for specifying whether a tensor is the input or output to the
/// model.
typedef enum TfLiteIoType {
kTfLiteIoTypeUnknown = 0,
kTfLiteIoTypeInput = 1,
kTfLiteIoTypeOutput = 2,
} TfLiteIoType;
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_C_TYPES_H_
+66
View File
@@ -0,0 +1,66 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
# Libraries to support TfLite buffer / synchronization interoperability.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_test(
name = "reconcile_fns_test",
srcs = ["reconcile_fns_test.cc"],
deps = [
":attribute_map_internal",
"//tensorflow/lite/core/async/interop/c:types",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "variant",
srcs = ["variant.cc"],
hdrs = ["variant.h"],
compatible_with = get_compatible_with_portable(),
)
cc_test(
name = "variant_test",
srcs = ["variant_test.cc"],
deps = [
":variant",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "attribute_map_internal_test",
srcs = ["attribute_map_internal_test.cc"],
deps = [
":attribute_map_internal",
"//tensorflow/lite/core/async/interop/c:types",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "attribute_map_internal",
srcs = [
"attribute_map_internal.cc",
"reconcile_fns.cc",
],
hdrs = [
"attribute_map_internal.h",
"reconcile_fns.h",
],
compatible_with = get_compatible_with_portable(),
deps = [
":variant",
"//tensorflow/lite/core/async/interop/c:types",
],
)
@@ -0,0 +1,48 @@
/* 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/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/reconcile_fns.h"
namespace tflite {
namespace interop {
bool AttributeMap::ReconcileAttributes(const AttributeMap* other,
AttributeMap* merged,
AttributeMap* conflict) const {
if (other == nullptr || merged == nullptr) return false;
if (type_ != other->type_) return false;
merged->type_ = type_;
if (conflict) conflict->type_ = type_;
// TODO(b/191883048): Reconcile custom keys.
return tflite::interop::ReconcileGeneralAttributeKeys(
type_, &attrs_, &other->attrs_, &merged->attrs_,
conflict ? &conflict->attrs_ : nullptr);
}
bool AttributeMap::CheckAttributeCoverage(const AttributeMap* other,
AttributeMap* conflict) const {
if (other == nullptr) return false;
if (type_ != other->type_) return false;
if (conflict) conflict->type_ = type_;
// TODO(b/191883048): Check custom key coverage.
return tflite::interop::CheckGeneralAttributeKeysCoverage(
type_, &attrs_, &other->attrs_, conflict ? &conflict->attrs_ : nullptr);
}
} // namespace interop
} // namespace tflite
@@ -0,0 +1,119 @@
/* 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_CORE_ASYNC_INTEROP_ATTRIBUTE_MAP_INTERNAL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_ATTRIBUTE_MAP_INTERNAL_H_
#include <cstdint>
#include <map>
#include <string>
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/async/interop/variant.h"
namespace tflite {
namespace interop {
// A value type pruned map, containing the attributes describing the properties
// of a backend buffer or synchronization object.
class AttributeMap {
public:
explicit AttributeMap(TfLiteAttrMapType type) : type_(type) {}
using KeyT = uint32_t;
using CustomKeyT = std::string;
// TODO(b/191883048): Benchmark std::variant vs. tagged union.
using ValueT = tflite::interop::Variant;
// TODO(b/191883048): Currently the number of attributes is small enough.
// So it's possible to optimize with a flat map.
using ContainerT = std::map<KeyT, ValueT>;
using CustomContainerT = std::map<CustomKeyT, ValueT>;
bool IsBufferAttributeMap() const {
return type_ == kTfLiteAttrMapTypeBuffer;
}
bool IsSyncAttributeMap() const { return type_ == kTfLiteAttrMapTypeSync; }
// Reconciles and merges the attribute values from other.
// After reconciliation, the merged value is compatible with both *this and
// `other`. e.g. a merged buffer size will be the maximum of two operands.
// If there's any attributes that cannot be reconciled, it will be filled to
// `conflict` if provided.
// `other` and `merged` should not be nullptr.
// Returns true if there's no conflicting attributes.
bool ReconcileAttributes(const AttributeMap* other, AttributeMap* merged,
AttributeMap* conflict) const;
// Checks if the attributes fully covers requirements.
// An attribute covers if the values are compatible or it only appears
// in *this.
// `other` should not be nullptr otherwise will return false.
// Returns true if attrs completely covers requirements.
bool CheckAttributeCoverage(const AttributeMap* other,
AttributeMap* conflict) const;
// Retrieves attribute value by key.
// Returns true if corresponding attribute exists and requested type matches,
// otherwise returns false.
template <typename AttrKeyT, typename ValueT>
bool GetAttr(AttrKeyT key, ValueT* value) const {
if (auto it = attrs_.find(static_cast<uint32_t>(key)); it != attrs_.end()) {
if (auto* v = it->second.Get<ValueT>(); v != nullptr) {
*value = *v;
return true;
}
}
return false;
}
// Sets attribute value by key.
template <typename AttrKeyT, typename ValueT>
void SetAttr(AttrKeyT key, ValueT value) {
attrs_.insert_or_assign(static_cast<KeyT>(key), value);
}
// Retrieves custom attribute value by key.
// Returns true if corresponding attribute exists, otherwise returns false.
template <typename ValueT>
bool GetCustomAttr(CustomKeyT key, ValueT* value) const {
if (auto it = custom_attrs_.find(key); it != custom_attrs_.end()) {
if (auto* v = it->second.Get<ValueT>(); v != nullptr) {
*value = *v;
return true;
}
}
return false;
}
// Sets custom attribute value by key.
template <typename ValueT>
void SetCustomAttr(CustomKeyT key, ValueT value) {
custom_attrs_.insert_or_assign(key, value);
}
private:
TfLiteAttrMapType type_;
ContainerT attrs_;
CustomContainerT custom_attrs_;
};
} // namespace interop
} // namespace tflite
struct TfLiteAttributeMap {
explicit TfLiteAttributeMap(TfLiteAttrMapType type) : impl(type) {}
tflite::interop::AttributeMap impl;
};
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_ATTRIBUTE_MAP_INTERNAL_H_
@@ -0,0 +1,125 @@
/* 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/core/async/interop/attribute_map_internal.h"
#include <cstddef>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace tflite {
namespace interop {
namespace {
TEST(AttributeMapTest, TypeTest) {
{
auto attrs = AttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_TRUE(attrs.IsBufferAttributeMap());
EXPECT_FALSE(attrs.IsSyncAttributeMap());
}
{
auto attrs = AttributeMap(kTfLiteAttrMapTypeSync);
EXPECT_TRUE(attrs.IsSyncAttributeMap());
EXPECT_FALSE(attrs.IsBufferAttributeMap());
}
}
TEST(AttributeMapTest, AccessorTest) {
auto attrs = AttributeMap(kTfLiteAttrMapTypeBuffer);
{
attrs.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(8));
size_t result;
EXPECT_TRUE(attrs.GetAttr(kTfLiteBufferAttrKeyAlignment, &result));
EXPECT_EQ(8, result);
}
{
attrs.SetCustomAttr("Foo", 12);
int result;
EXPECT_FALSE(attrs.GetCustomAttr("Bar", &result));
EXPECT_TRUE(attrs.GetCustomAttr("Foo", &result));
EXPECT_EQ(12, result);
}
}
TEST(AttributeMapTest, ReconcileFailDifferentTypes) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeSync);
auto attrs3 = AttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_FALSE(
attrs1.ReconcileAttributes(&attrs2, &attrs3, /*conflict=*/nullptr));
EXPECT_FALSE(attrs1.CheckAttributeCoverage(&attrs2, &attrs3));
}
TEST(AttributeMapTest, NullptrTest) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_FALSE(attrs1.ReconcileAttributes(/*other=*/nullptr, &attrs2,
/*conflict=*/nullptr));
EXPECT_FALSE(attrs1.ReconcileAttributes(&attrs2, /*merged=*/nullptr,
/*conflict=*/nullptr));
EXPECT_FALSE(attrs1.CheckAttributeCoverage(/*other=*/nullptr,
/*conflict=*/nullptr));
}
TEST(AttributeMapTest, ReconcileDifferentTypes) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeSync);
auto attrs3 = AttributeMap(kTfLiteAttrMapTypeBuffer);
EXPECT_FALSE(attrs1.ReconcileAttributes(&attrs2, &attrs3,
/*conflict=*/nullptr));
}
TEST(AttributeMapTest, ReconcileTest) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs1.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(8));
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs2.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(4));
auto attrs3 = AttributeMap(kTfLiteAttrMapTypeSync);
auto attrs4 = AttributeMap(kTfLiteAttrMapTypeSync);
EXPECT_TRUE(attrs1.ReconcileAttributes(&attrs2, &attrs3, &attrs4));
EXPECT_TRUE(attrs3.IsBufferAttributeMap());
EXPECT_TRUE(attrs4.IsBufferAttributeMap());
size_t result;
EXPECT_TRUE(attrs3.GetAttr(kTfLiteBufferAttrKeyAlignment, &result));
EXPECT_EQ(8, result);
}
TEST(AttributeMapTest, CoverageTest) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs1.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(8));
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs2.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(4));
auto attrs3 = AttributeMap(kTfLiteAttrMapTypeSync);
EXPECT_TRUE(attrs1.CheckAttributeCoverage(&attrs2, &attrs3));
EXPECT_TRUE(attrs3.IsBufferAttributeMap());
}
TEST(AttributeMapTest, CoverageFailedTest) {
auto attrs1 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs1.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(10));
auto attrs2 = AttributeMap(kTfLiteAttrMapTypeBuffer);
attrs2.SetAttr(kTfLiteBufferAttrKeyAlignment, size_t(4));
auto conflict = AttributeMap(kTfLiteAttrMapTypeSync);
EXPECT_FALSE(attrs1.CheckAttributeCoverage(&attrs2, &conflict));
EXPECT_TRUE(conflict.IsBufferAttributeMap());
size_t result;
EXPECT_TRUE(conflict.GetAttr(kTfLiteBufferAttrKeyAlignment, &result));
EXPECT_EQ(4, result);
}
} // namespace
} // namespace interop
} // namespace tflite
@@ -0,0 +1,62 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load("//tensorflow/lite:build_def.bzl", "tflite_copts")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "types",
srcs = ["types.cc"],
hdrs = ["types.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
)
cc_library(
name = "attribute_map",
srcs = ["attribute_map.cc"],
hdrs = ["attribute_map.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
deps = [
":types",
"//tensorflow/lite/core/async/interop:attribute_map_internal",
],
)
cc_test(
name = "attribute_map_test",
srcs = ["attribute_map_test.cc"],
deps = [
":attribute_map",
":types",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "types_test",
srcs = ["types_test.cc"],
deps = [
":types",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "constants",
srcs = ["constants.cc"],
hdrs = ["constants.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
deps = [
"//tensorflow/lite/core/c:c_api_types",
],
)
@@ -0,0 +1,143 @@
/* 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/core/async/interop/c/attribute_map.h"
#include <cstddef>
#include <cstdint>
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
extern "C" {
TfLiteAttributeMap* TfLiteAttributeMapCreate(TfLiteAttrMapType type) {
return new TfLiteAttributeMap(type);
}
void TfLiteAttributeMapDelete(TfLiteAttributeMap* attrs) { delete attrs; }
bool TfLiteAttributeMapIsBufferAttributeMap(const TfLiteAttributeMap* attrs) {
if (attrs) return attrs->impl.IsBufferAttributeMap();
return false;
}
bool TfLiteAttributeMapIsSyncAttributeMap(const TfLiteAttributeMap* attrs) {
if (attrs) return attrs->impl.IsSyncAttributeMap();
return false;
}
void TfLiteAttributeMapCopy(const TfLiteAttributeMap* src,
TfLiteAttributeMap* dst) {
if (src && dst) {
dst->impl = src->impl;
}
}
bool TfLiteAttributeMapGetSizeTBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key,
size_t* val) {
return attrs && attrs->impl.IsBufferAttributeMap() &&
attrs->impl.GetAttr(key, val);
}
bool TfLiteAttributeMapSetSizeTBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, size_t val) {
if (attrs && attrs->impl.IsBufferAttributeMap()) {
attrs->impl.SetAttr(key, val);
return true;
}
return false;
}
bool TfLiteAttributeMapGetStringBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key,
const char** val) {
return attrs && attrs->impl.IsBufferAttributeMap() &&
attrs->impl.GetAttr(key, val);
}
bool TfLiteAttributeMapSetStringBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key,
const char* val) {
if (attrs && attrs->impl.IsBufferAttributeMap()) {
attrs->impl.SetAttr(key, val);
return true;
}
return false;
}
bool TfLiteAttributeMapGetBoolBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, bool* val) {
return attrs && attrs->impl.IsBufferAttributeMap() &&
attrs->impl.GetAttr(key, val);
}
bool TfLiteAttributeMapSetBoolBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, bool val) {
if (attrs && attrs->impl.IsBufferAttributeMap()) {
attrs->impl.SetAttr(key, val);
return true;
}
return false;
}
bool TfLiteAttributeMapGetStringSyncAttr(const TfLiteAttributeMap* attrs,
TfLiteSynchronizationAttrKey key,
const char** val) {
return attrs && attrs->impl.IsSyncAttributeMap() &&
attrs->impl.GetAttr(key, val);
}
bool TfLiteAttributeMapSetStringSyncAttr(TfLiteAttributeMap* attrs,
TfLiteSynchronizationAttrKey key,
const char* val) {
if (attrs && attrs->impl.IsSyncAttributeMap()) {
attrs->impl.SetAttr(key, val);
return true;
}
return false;
}
// DEPRECATED. Do not use.
#define DEFINE_ATTR_MAP_ACCESSOR(type, type_name) \
bool TfLiteAttributeMapGet##type_name##Attr(const TfLiteAttributeMap* attrs, \
uint32_t key, type* val) { \
return attrs ? attrs->impl.GetAttr(static_cast<TfLiteBufferAttrKey>(key), \
val) \
: false; \
} \
void TfLiteAttributeMapSet##type_name##Attr(TfLiteAttributeMap* attrs, \
uint32_t key, type val) { \
if (attrs) { \
attrs->impl.SetAttr(static_cast<TfLiteBufferAttrKey>(key), val); \
} \
} \
bool TfLiteAttributeMapGetCustom##type_name##Attr( \
const TfLiteAttributeMap* attrs, const char* key, type* val) { \
return attrs ? attrs->impl.GetCustomAttr(key, val) : false; \
} \
void TfLiteAttributeMapSetCustom##type_name##Attr( \
TfLiteAttributeMap* attrs, const char* key, type val) { \
if (attrs) { \
attrs->impl.SetCustomAttr(key, val); \
} \
}
DEFINE_ATTR_MAP_ACCESSOR(int, Int);
DEFINE_ATTR_MAP_ACCESSOR(size_t, SizeT);
DEFINE_ATTR_MAP_ACCESSOR(const char*, String);
DEFINE_ATTR_MAP_ACCESSOR(bool, Bool);
#undef DEFINE_ATTR_MAP_ACCESSOR
} // extern "C"
@@ -0,0 +1,158 @@
/* 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_CORE_ASYNC_INTEROP_C_ATTRIBUTE_MAP_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_ATTRIBUTE_MAP_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "tensorflow/lite/core/async/interop/c/types.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// TfLiteAttributeMap API.
///
/// TfLiteAttributeMap stores buffer or sync attributes and keeps those
/// intelligible across different backends and applications.
/// Backend delegates can define a set of attribute keys to describe what's
/// the attribute and defines the type of the value.
/// Different components (application, TfLite runtime, backends) can use
/// TfLiteAttributeMap to negotiate the requirements of the buffer / sync
/// and establish the contract on specifications of a particular input / output.
/// WARNING: This is an experimental type and subject to change.
/// Opaque type for TfLiteAttributeMap.
typedef struct TfLiteAttributeMap TfLiteAttributeMap;
/// Creates an attribute map.
/// `type` argument determines what the attribute map is describing
/// (e.g. buffer, or sync object).
/// Returned object is owned by the caller.
TfLiteAttributeMap* TfLiteAttributeMapCreate(TfLiteAttrMapType type);
/// Destroys the attribute map.
/// Do nothing if `attrs` is nullptr.
void TfLiteAttributeMapDelete(TfLiteAttributeMap* attrs);
/// Returns true if `attrs` is a buffer attribute map.
/// If `attrs` is nullptr, returns false.
bool TfLiteAttributeMapIsBufferAttributeMap(const TfLiteAttributeMap* attrs);
/// Returns true if `attrs` is a sync object attribute map.
/// If `attrs` is nullptr, returns false.
bool TfLiteAttributeMapIsSyncAttributeMap(const TfLiteAttributeMap* attrs);
/// Copies all attributes from `src` to `dst`. Any existing attributes in `dst`
/// will be cleared.
/// If `src` or `dst` is null, does nothing.
void TfLiteAttributeMapCopy(const TfLiteAttributeMap* src,
TfLiteAttributeMap* dst);
// --------------------------------------------------------------------------
/// Accessor methods.
///
/// For getters, returns false if the key is not set, or the requested type
/// does not match.
/// For setters, if the value type is a pointer (e.g. c string literals),
/// caller needs to ensure the lifetime of value exceeds the attribute map.
/// If the key is set in previous calls, old value will be overriden by
/// successive setter calls.
/// Gets the int buffer attribute value for the given `key`.
/// Returns false if the key is not set, `attrs` is not a buffer attribute map,
/// or the value is not of type `size_t`.
bool TfLiteAttributeMapGetSizeTBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, size_t* val);
/// Sets the `key` buffer attribute as `val`.
/// Returns false if `attrs` is not a buffer attribute map.
bool TfLiteAttributeMapSetSizeTBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, size_t val);
/// Gets the C string buffer attribute value for the given `key`.
/// Returns false if the key is not set, `attrs` is not a buffer attribute map,
/// or the value is not of type `size_t`.
/// Returned C string's lifespan is determined by the setter of that value.
/// Neither `attrs` nor the caller maintains the lifespan of the string.
bool TfLiteAttributeMapGetStringBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key,
const char** val);
/// Sets the `key` buffer attribute as `val`.
/// Returns false if `attrs` is not a buffer attribute map.
/// `attrs` does not own the `val` C string.
bool TfLiteAttributeMapSetStringBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key,
const char* val);
/// Gets the bool buffer attribute value for the given `key`.
/// Returns false if the key is not set, `attrs` is not a buffer attribute map,
/// or the value is not of type `bool`.
bool TfLiteAttributeMapGetBoolBufferAttr(const TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, bool* val);
/// Sets the `key` buffer attribute as `val`.
/// Returns false if `attrs` is not a sync attribute map.
/// `attrs` does not own the `val` C string.
bool TfLiteAttributeMapSetBoolBufferAttr(TfLiteAttributeMap* attrs,
TfLiteBufferAttrKey key, bool val);
/// Gets the C string synchronization attribute value for the given `key`.
/// Returns false if the key is not set, `attrs` is not a sync attribute map,
/// or the value is not of type `size_t`.
/// Returned C string's lifespan is determined by the setter of that value.
/// Neither `attrs` nor the caller maintains the lifespan of the string.
bool TfLiteAttributeMapGetStringSyncAttr(const TfLiteAttributeMap* attrs,
TfLiteSynchronizationAttrKey key,
const char** val);
/// Sets the `key` buffer attribute as `val`.
/// Returns false if `attrs` is not a sync attribute map.
/// `attrs` does not own the `val` C string.
bool TfLiteAttributeMapSetStringSyncAttr(TfLiteAttributeMap* attrs,
TfLiteSynchronizationAttrKey key,
const char* val);
/// \privatesection
/// Attribute map accessor methods that does not check the map type.
/// It's recommended to use methods above for setting / getting attribute values
/// as those will also check whether the attribute key matches the attribute
/// map type.
#define DECLARE_ATTR_MAP_ACCESSOR(type, type_name) \
bool TfLiteAttributeMapGet##type_name##Attr(const TfLiteAttributeMap* attrs, \
uint32_t key, type* val); \
void TfLiteAttributeMapSet##type_name##Attr(TfLiteAttributeMap* attrs, \
uint32_t key, type val); \
bool TfLiteAttributeMapGetCustom##type_name##Attr( \
const TfLiteAttributeMap* attrs, const char* key, type* val); \
void TfLiteAttributeMapSetCustom##type_name##Attr( \
TfLiteAttributeMap* attrs, const char* key, type val);
DECLARE_ATTR_MAP_ACCESSOR(int, Int);
DECLARE_ATTR_MAP_ACCESSOR(size_t, SizeT);
DECLARE_ATTR_MAP_ACCESSOR(const char*, String);
DECLARE_ATTR_MAP_ACCESSOR(bool, Bool);
#undef DECLARE_ATTR_MAP_ACCESSOR
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_ATTRIBUTE_MAP_H_
@@ -0,0 +1,143 @@
/* 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/core/async/interop/c/attribute_map.h"
#include <cstddef>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace {
TEST(AttributeMapTest, AttributeMapCreateTypeCheckTest) {
{
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
EXPECT_TRUE(TfLiteAttributeMapIsBufferAttributeMap(attr));
EXPECT_FALSE(TfLiteAttributeMapIsSyncAttributeMap(attr));
TfLiteAttributeMapDelete(attr);
}
{
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeSync);
EXPECT_FALSE(TfLiteAttributeMapIsBufferAttributeMap(attr));
EXPECT_TRUE(TfLiteAttributeMapIsSyncAttributeMap(attr));
TfLiteAttributeMapDelete(attr);
}
}
TEST(AttributeMapTest, AttributeMapAccessor) {
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
{
TfLiteAttributeMapSetSizeTBufferAttr(attr, kTfLiteBufferAttrKeyAlignment,
42);
size_t result = 0;
EXPECT_TRUE(TfLiteAttributeMapGetSizeTBufferAttr(
attr, kTfLiteBufferAttrKeyAlignment, &result));
EXPECT_EQ(42, result);
EXPECT_FALSE(TfLiteAttributeMapGetSizeTBufferAttr(
attr, kTfLiteBufferAttrKeyOffset, &result));
}
{
const char str[] = "some string";
// Overriding key 1.
TfLiteAttributeMapSetStringBufferAttr(
attr, kTfLiteBufferAttrKeyResourceTypeName, str);
const char* result = nullptr;
EXPECT_TRUE(TfLiteAttributeMapGetStringBufferAttr(
attr, kTfLiteBufferAttrKeyResourceTypeName, &result));
EXPECT_EQ(str, result);
EXPECT_FALSE(TfLiteAttributeMapGetStringBufferAttr(
attr, kTfLiteBufferAttrKeyAlignment, &result));
EXPECT_FALSE(TfLiteAttributeMapSetStringSyncAttr(
attr, kTfLiteSynchronizationAttrKeyObjectTypeName, str));
EXPECT_FALSE(TfLiteAttributeMapGetStringSyncAttr(
attr, kTfLiteSynchronizationAttrKeyObjectTypeName, &result));
}
TfLiteAttributeMapDelete(attr);
}
TEST(AttributeMapTest, UnCheckedAttributeMapAccessor) {
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
{
TfLiteAttributeMapSetSizeTAttr(attr, 1, 42);
size_t result = 0;
EXPECT_TRUE(TfLiteAttributeMapGetSizeTAttr(attr, 1, &result));
EXPECT_EQ(42, result);
EXPECT_FALSE(TfLiteAttributeMapGetSizeTAttr(attr, 2, &result));
}
{
TfLiteAttributeMapSetIntAttr(attr, 3, 21);
int result = 0;
EXPECT_TRUE(TfLiteAttributeMapGetIntAttr(attr, 3, &result));
EXPECT_EQ(21, result);
EXPECT_FALSE(TfLiteAttributeMapGetIntAttr(attr, 4, &result));
}
{
const char str[] = "some string";
// Overriding key 1.
TfLiteAttributeMapSetStringAttr(attr, 1, str);
const char* result = nullptr;
EXPECT_TRUE(TfLiteAttributeMapGetStringAttr(attr, 1, &result));
EXPECT_EQ(str, result);
EXPECT_FALSE(TfLiteAttributeMapGetStringAttr(attr, 2, &result));
}
{
TfLiteAttributeMapSetBoolAttr(
attr, kTfLiteBufferAttrKeyCurrentHostCoherencyState, true);
bool result = false;
EXPECT_TRUE(TfLiteAttributeMapGetBoolAttr(
attr, kTfLiteBufferAttrKeyCurrentHostCoherencyState, &result));
EXPECT_TRUE(result);
EXPECT_FALSE(TfLiteAttributeMapGetBoolAttr(
attr, kTfLiteBufferAttrKeyPreferredHostCoherencyState, &result));
}
TfLiteAttributeMapDelete(attr);
}
TEST(AttributeMapTest, UnCheckedAttributeMapCustomAccessor) {
auto* attr = TfLiteAttributeMapCreate(kTfLiteAttrMapTypeBuffer);
{
TfLiteAttributeMapSetCustomSizeTAttr(attr, "foo", 42);
size_t result = 0;
EXPECT_TRUE(TfLiteAttributeMapGetCustomSizeTAttr(attr, "foo", &result));
EXPECT_EQ(42, result);
EXPECT_FALSE(TfLiteAttributeMapGetCustomSizeTAttr(attr, "bar", &result));
}
{
TfLiteAttributeMapSetCustomIntAttr(attr, "baz", 21);
int result = 0;
EXPECT_TRUE(TfLiteAttributeMapGetCustomIntAttr(attr, "baz", &result));
EXPECT_EQ(21, result);
EXPECT_FALSE(TfLiteAttributeMapGetCustomIntAttr(attr, "quux", &result));
}
{
const char str[] = "some string";
// Overriding key "foo".
TfLiteAttributeMapSetCustomStringAttr(attr, "foo", str);
const char* result = nullptr;
EXPECT_TRUE(TfLiteAttributeMapGetCustomStringAttr(attr, "foo", &result));
EXPECT_EQ(str, result);
EXPECT_FALSE(TfLiteAttributeMapGetCustomStringAttr(attr, "bar", &result));
}
{
TfLiteAttributeMapSetCustomBoolAttr(attr, "foo", true);
bool result = false;
EXPECT_TRUE(TfLiteAttributeMapGetCustomBoolAttr(attr, "foo", &result));
EXPECT_TRUE(result);
EXPECT_FALSE(TfLiteAttributeMapGetCustomBoolAttr(attr, "bar", &result));
}
TfLiteAttributeMapDelete(attr);
}
} // namespace
@@ -0,0 +1,21 @@
/* 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/core/async/interop/c/constants.h"
extern "C" {
const char kTfLiteSyncTypeNoSyncObj[] = "no_sync_obj";
} // extern "C"
@@ -0,0 +1,45 @@
/* 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_CORE_ASYNC_INTEROP_C_CONSTANTS_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_CONSTANTS_H_
#include "tensorflow/lite/core/c/c_api_types.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// Constants for TensorFlow Lite Async API.
///
/// WARNING: This is an experimental type and subject to change.
/// Synchronization type name of "no synchronization object".
///
/// This is the default synchronization type for tensors that do not have
/// user-specified synchronization attributes.
/// When set on input tensors, the backend must ignore any input synchronization
/// objects provided by the user, and the buffer content of the input tensor
/// must be ready when AsyncSignatureRunner::InvokeAsync is called.
/// When set on output tensors, the backend must not provide any output
/// synchronization objects back to the user, and the buffer content of the
/// output tensor must be ready when AsyncSignatureRunner::Wait returns.
TFL_CAPI_EXPORT extern const char kTfLiteSyncTypeNoSyncObj[]; // "no_sync_obj"
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_CONSTANTS_H_
@@ -0,0 +1,51 @@
/* 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/core/async/interop/c/types.h"
struct TfLiteBackendBuffer {
void* ptr = nullptr;
};
struct TfLiteSynchronization {
void* ptr = nullptr;
};
extern "C" {
TfLiteBackendBuffer* TfLiteBackendBufferCreate() {
return new TfLiteBackendBuffer;
}
void TfLiteBackendBufferDelete(TfLiteBackendBuffer* buf) { delete buf; }
void TfLiteBackendBufferSetPtr(TfLiteBackendBuffer* buf, void* ptr) {
buf->ptr = ptr;
}
void* TfLiteBackendBufferGetPtr(const TfLiteBackendBuffer* buf) {
return buf->ptr;
}
TfLiteSynchronization* TfLiteSynchronizationCreate() {
return new TfLiteSynchronization;
}
void TfLiteSynchronizationDelete(TfLiteSynchronization* sync) { delete sync; }
void TfLiteSynchronizationSetPtr(TfLiteSynchronization* sync, void* ptr) {
sync->ptr = ptr;
}
void* TfLiteSynchronizationGetPtr(const TfLiteSynchronization* sync) {
return sync->ptr;
}
} // extern "C"
@@ -0,0 +1,132 @@
/* 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_CORE_ASYNC_INTEROP_C_TYPES_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_TYPES_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// Types for hardware buffer object / synchronization object interoperability.
/// WARNING: This is an experimental type and subject to change.
/// TfLiteBackendBuffer is a an opaque type that abstracts platform specific
/// implementations of hardware buffer objects (e.g. AHardwareBuffer).
/// It's used for carrying the platform-specific hardware buffer object across
/// applications, TFLite runtime and backends.
typedef struct TfLiteBackendBuffer TfLiteBackendBuffer;
/// Creates an empty TfLiteBackendBuffer that does not contain any hardware
/// buffers object.
/// Returned object is owned by the caller.
TfLiteBackendBuffer* TfLiteBackendBufferCreate();
/// Destroys a TfLiteBackendBuffer.
/// Calling this function will not release the buffer object stored underneath.
void TfLiteBackendBufferDelete(TfLiteBackendBuffer* buf);
/// Stores a type puned buffer object to TfLiteBackendBuffer.
/// `buf` will not own or control the lifecycle of `ptr`.
/// Callers needs to ensure lifetime of *ptr exceeds `buf`.
void TfLiteBackendBufferSetPtr(TfLiteBackendBuffer* buf, void* ptr);
/// Retrieves the buffer object from TfLiteBackendBuffer.
/// Callers can use TfLiteAttributeMap buffer type name to interpret returned
/// pointer.
void* TfLiteBackendBufferGetPtr(const TfLiteBackendBuffer* buf);
/// TfLiteSynchronization is an opaque type that abstracts platform specific
/// implementations of synchronization objects. It's used for carrying the
/// synchronization object across applications, TFLite runtime and backends.
typedef struct TfLiteSynchronization TfLiteSynchronization;
/// Creates an empty TfLiteSynchronization.
/// Returned object is owned by the caller.
TfLiteSynchronization* TfLiteSynchronizationCreate();
/// Destroys a TfLiteSynchronization.
/// Calling this function will not release the synchronization object stored.
void TfLiteSynchronizationDelete(TfLiteSynchronization* sync);
/// Stores a type-punned pointer to a platform-specific synchronization object.
/// `sync` will not own or control the lifecycle of `ptr`.
/// Callers needs to ensure lifetime of *ptr exceeds `sync`.
void TfLiteSynchronizationSetPtr(TfLiteSynchronization* sync, void* ptr);
/// Retrieves the sync object from TfLiteSynchronization.
/// Callers can use TfLiteAttributeMap sync type name to interpret returned
/// pointer.
void* TfLiteSynchronizationGetPtr(const TfLiteSynchronization* sync);
/// Type of the attribute map.
/// An attribute map can either describe the properties of backend buffers
/// or synchronizations.
/// The value of the TfLiteAttrMapType determines the interpretation of
/// attribute keys. See comments below.
typedef enum TfLiteAttrMapType {
/// Unknown type.
kTfLiteAttrMapTypeUnknown = 0,
/// The attributes describes a platform-specific hardware buffer object (e.g.
/// AHardwareBuffer for Android).
/// Keys are of TfLiteBufferAttrKey type.
kTfLiteAttrMapTypeBuffer = 1,
/// The attributes describes a sync object (e.g. a file descriptor as sync
/// fence).
/// Keys are of TfLiteSynchronizationAttrKey type.
kTfLiteAttrMapTypeSync = 2,
} TfLiteAttrMapType;
/// General hardware buffer attribute keys that are recognizable by TFLite.
typedef enum TfLiteBufferAttrKey {
kTfLiteBufferAttrKeyUnknown = 0,
/// Backing buffer resource. const char*
/// e.g. "AHardwareBuffer".
kTfLiteBufferAttrKeyResourceTypeName = 1,
/// Buffer alignment, size_t
kTfLiteBufferAttrKeyAlignment = 2,
/// Buffer padding, size_t
kTfLiteBufferAttrKeyPadding = 3,
/// Buffer offset, size_t
kTfLiteBufferAttrKeyOffset = 4,
/// Buffer size (padded size if applicable), size_t
kTfLiteBufferAttrKeySize = 5,
/// Buffer current host coherency state, bool
kTfLiteBufferAttrKeyCurrentHostCoherencyState = 6,
/// Buffer preferred host coherency state, bool
kTfLiteBufferAttrKeyPreferredHostCoherencyState = 7,
/// Buffer current host cache state, bool
kTfLiteBufferAttrKeyCurrentHostCacheState = 8,
/// Buffer preferred cache state, bool
kTfLiteBufferAttrKeyPreferredHostCacheState = 9,
} TfLiteBufferAttrKey;
/// General synchronization attribute keys that are recognizable by TFLite.
typedef enum TfLiteSynchronizationAttrKey {
kTfLiteSynchronizationAttrKeyUnknown = 0,
/// Synchronization type name. const char*
/// e.g. "sync_fence_fd"
kTfLiteSynchronizationAttrKeyObjectTypeName = 1,
} TfLiteSynchronizationAttrKey;
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_C_TYPES_H_
@@ -0,0 +1,53 @@
/* 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/core/async/interop/c/types.h"
#include <gtest/gtest.h>
namespace {
TEST(TypesTest, TfLiteBackendBuffer) {
auto* tflite_buf = TfLiteBackendBufferCreate();
EXPECT_EQ(nullptr, TfLiteBackendBufferGetPtr(tflite_buf));
char buf[42];
TfLiteBackendBufferSetPtr(tflite_buf, buf);
EXPECT_EQ(buf, TfLiteBackendBufferGetPtr(tflite_buf));
char another_buf[7];
TfLiteBackendBufferSetPtr(tflite_buf, another_buf);
EXPECT_EQ(another_buf, TfLiteBackendBufferGetPtr(tflite_buf));
TfLiteBackendBufferDelete(tflite_buf);
}
TEST(TypesTest, TfLiteSynchronization) {
auto* tflite_sync = TfLiteSynchronizationCreate();
EXPECT_EQ(nullptr, TfLiteSynchronizationGetPtr(tflite_sync));
int fd = 42;
TfLiteSynchronizationSetPtr(tflite_sync, &fd);
EXPECT_EQ(&fd, TfLiteSynchronizationGetPtr(tflite_sync));
double sync = 7.11;
TfLiteSynchronizationSetPtr(tflite_sync, &sync);
EXPECT_EQ(&sync, TfLiteSynchronizationGetPtr(tflite_sync));
TfLiteSynchronizationDelete(tflite_sync);
}
} // namespace
@@ -0,0 +1,187 @@
/* 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/core/async/interop/reconcile_fns.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <set>
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace tflite {
namespace interop {
namespace {
// TODO(b/191883048): Check binary size impact with <numeric> header and replace
// with std::lcm if possible.
template <typename T>
T gcd(T x, T y) {
while (y) {
auto m = x % y;
x = y;
y = m;
}
return x;
}
template <typename T>
T lcm(T x, T y) {
return x / gcd(x, y) * y;
}
// Reconciled alignment is LCM of l and r.
void ReconcileAlignment(size_t l, size_t r, AttributeMap::ContainerT* merged) {
merged->insert_or_assign(static_cast<size_t>(kTfLiteBufferAttrKeyAlignment),
lcm(l, r));
}
// Reconciled padding is LCM of l and r.
void ReconcilePadding(size_t l, size_t r, AttributeMap::ContainerT* merged) {
merged->insert_or_assign(static_cast<size_t>(kTfLiteBufferAttrKeyPadding),
lcm(l, r));
}
// For alignment and padding, if l is multiples of r, it's covering r.
bool CheckMultiples(size_t l, size_t r) { return l % r == 0; }
// Reconciled size is max(l, r).
void ReconcileSize(size_t l, size_t r, AttributeMap::ContainerT* merged) {
merged->insert_or_assign(static_cast<size_t>(kTfLiteBufferAttrKeySize),
std::max(l, r));
}
// Checks if l >= r.
bool CheckSize(size_t l, size_t r) { return l >= r; }
} // namespace
bool ReconcileGeneralAttributeKeys(TfLiteAttrMapType type,
const AttributeMap::ContainerT* lhs,
const AttributeMap::ContainerT* rhs,
AttributeMap::ContainerT* merged,
AttributeMap::ContainerT* conflict) {
if (lhs == nullptr || rhs == nullptr || merged == nullptr) return false;
bool ret = true;
std::set<uint32_t> keys;
std::transform(lhs->begin(), lhs->end(), std::inserter(keys, keys.end()),
[](auto pair) { return pair.first; });
std::transform(rhs->begin(), rhs->end(), std::inserter(keys, keys.end()),
[](auto pair) { return pair.first; });
for (auto k : keys) {
const auto l = lhs->find(k);
const auto r = rhs->find(k);
if (l == lhs->end() || l->second.GetPtr() == nullptr) {
merged->insert_or_assign(k, r->second);
continue;
}
if (r == rhs->end() || r->second.GetPtr() == nullptr) {
merged->insert_or_assign(k, l->second);
continue;
}
if (type == kTfLiteAttrMapTypeBuffer) {
switch (static_cast<TfLiteBufferAttrKey>(k)) {
case kTfLiteBufferAttrKeySize:
ReconcileSize(*l->second.Get<size_t>(), *r->second.Get<size_t>(),
merged);
break;
case kTfLiteBufferAttrKeyAlignment:
ReconcileAlignment(*l->second.Get<size_t>(), *r->second.Get<size_t>(),
merged);
break;
case kTfLiteBufferAttrKeyPadding:
ReconcilePadding(*l->second.Get<size_t>(), *r->second.Get<size_t>(),
merged);
break;
default:
// For other keys, check equality.
if (l->second == r->second) {
merged->insert_or_assign(k, l->second);
} else {
ret = false;
if (conflict) conflict->insert_or_assign(k, r->second);
}
}
} else {
// Check equality.
if (l->second == r->second) {
merged->insert_or_assign(k, l->second);
} else {
ret = false;
if (conflict) conflict->insert_or_assign(k, r->second);
}
}
}
return ret;
}
bool CheckGeneralAttributeKeysCoverage(TfLiteAttrMapType type,
const AttributeMap::ContainerT* lhs,
const AttributeMap::ContainerT* rhs,
AttributeMap::ContainerT* conflict) {
if (lhs == nullptr || rhs == nullptr) return false;
bool ret = true;
std::set<uint32_t> keys;
std::transform(lhs->begin(), lhs->end(), std::inserter(keys, keys.end()),
[](auto pair) { return pair.first; });
std::transform(rhs->begin(), rhs->end(), std::inserter(keys, keys.end()),
[](auto pair) { return pair.first; });
for (auto k : keys) {
bool has_conflict = false;
const auto l = lhs->find(k);
const auto r = rhs->find(k);
if (r == rhs->end() || r->second.GetPtr() == nullptr) {
continue;
} else if (l == lhs->end() || l->second.GetPtr() == nullptr) {
has_conflict = true;
} else {
if (type == kTfLiteAttrMapTypeBuffer) {
switch (static_cast<TfLiteBufferAttrKey>(k)) {
case kTfLiteBufferAttrKeySize:
has_conflict |=
!CheckSize(*l->second.Get<size_t>(), *r->second.Get<size_t>());
break;
case kTfLiteBufferAttrKeyAlignment:
has_conflict |= !CheckMultiples(*l->second.Get<size_t>(),
*r->second.Get<size_t>());
break;
case kTfLiteBufferAttrKeyPadding:
has_conflict |=
!CheckSize(*l->second.Get<size_t>(), *r->second.Get<size_t>());
break;
default:
// For other keys, check equality.
if (l->second != r->second) {
has_conflict = true;
}
}
} else {
if (l->second != r->second) {
has_conflict = true;
}
}
}
if (has_conflict) {
if (conflict != nullptr) conflict->insert_or_assign(k, r->second);
ret = false;
}
}
return ret;
}
} // namespace interop
} // namespace tflite
@@ -0,0 +1,49 @@
/* 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_CORE_ASYNC_INTEROP_RECONCILE_FNS_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_RECONCILE_FNS_H_
// Reconciliation functions for merging and examinate buffer / synchronization
// attributes.
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace tflite {
namespace interop {
// Reconciles general attributes.
// `lhs`, `rhs`, `merged` are required to be not null, otherwise return false.
// The merged attribute will be set in `merged`. If there's any attribute that
// can not be reconciled, it will be set in `conflict` and return false.
bool ReconcileGeneralAttributeKeys(TfLiteAttrMapType type,
const AttributeMap::ContainerT* lhs,
const AttributeMap::ContainerT* rhs,
AttributeMap::ContainerT* merged,
AttributeMap::ContainerT* conflict);
// Check if `lhs` covers all attribute in `rhs`.
// `lhs` and `rhs` are required to be not null, otherwise return false.
// If there's any attribute that is not covered (i.e. missing from `lhs` or
// values are incompatible), it will be set in `conflict` and return false.
bool CheckGeneralAttributeKeysCoverage(TfLiteAttrMapType type,
const AttributeMap::ContainerT* lhs,
const AttributeMap::ContainerT* rhs,
AttributeMap::ContainerT* conflict);
} // namespace interop
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_RECONCILE_FNS_H_
@@ -0,0 +1,296 @@
/* 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/core/async/interop/reconcile_fns.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <tuple>
#include <gtest/gtest.h>
#include "tensorflow/lite/core/async/interop/attribute_map_internal.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace tflite::interop {
namespace {
using ContainerT = AttributeMap::ContainerT;
template <typename ValT, typename KeyT>
void SetAttr(ContainerT* c, KeyT k, ValT v) {
c->insert_or_assign(static_cast<uint32_t>(k), v);
}
template <typename ValT, typename KeyT>
ValT GetAttr(const ContainerT& c, KeyT k) {
return *(c.at(static_cast<uint32_t>(k)).Get<ValT>());
}
TEST(ReconcileTest, NullCheck) {
ContainerT m1, m2;
// `merged` nullptr
EXPECT_FALSE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &m1, &m2,
/*merged=*/nullptr,
/*conflict=*/nullptr));
// `lhs` nullptr
EXPECT_FALSE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer,
/*lhs=*/nullptr, &m1, &m2,
/*conflict=*/nullptr));
// `rhs` nullptr
EXPECT_FALSE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &m1,
/*rhs=*/nullptr, &m2,
/*conflict=*/nullptr));
// `lhs` nullptr
EXPECT_FALSE(CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer,
/*lhs=*/nullptr, &m1, &m2));
// `rhs` nullptr
EXPECT_FALSE(CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer, &m1,
/*rhs=*/nullptr, &m2));
}
TEST(ReconcileTest, MissingAttributeTest) {
{
ContainerT lhs, rhs, merged;
SetAttr(&lhs, kTfLiteBufferAttrKeyAlignment, size_t(4));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(4, GetAttr<size_t>(merged, kTfLiteBufferAttrKeyAlignment));
}
{
ContainerT lhs, rhs, merged;
SetAttr(&rhs, kTfLiteBufferAttrKeyAlignment, size_t(4));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(4, GetAttr<size_t>(merged, kTfLiteBufferAttrKeyAlignment));
}
{
ContainerT lhs, rhs, merged;
const char value[] = "string";
SetAttr(&rhs, kTfLiteSynchronizationAttrKeyObjectTypeName, value);
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeSync, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(value, GetAttr<const char*>(
merged, kTfLiteSynchronizationAttrKeyObjectTypeName));
}
}
TEST(CheckCoverageTest, MissingAttributeTest) {
{
ContainerT lhs, rhs;
SetAttr(&lhs, kTfLiteBufferAttrKeyAlignment, size_t(4));
EXPECT_TRUE(CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer,
&lhs, &rhs, nullptr));
}
{
ContainerT lhs, rhs, merged;
SetAttr(&rhs, kTfLiteBufferAttrKeyAlignment, size_t(4));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_FALSE(CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer,
&lhs, &rhs, nullptr));
}
}
class ReconcileAlignmentTest
: public testing::TestWithParam<std::tuple<size_t, size_t, size_t>> {};
TEST_P(ReconcileAlignmentTest, Test) {
ContainerT lhs, rhs, merged;
SetAttr(&lhs, kTfLiteBufferAttrKeyAlignment, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeyAlignment, std::get<1>(GetParam()));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(std::get<2>(GetParam()),
GetAttr<size_t>(merged, kTfLiteBufferAttrKeyAlignment));
}
INSTANTIATE_TEST_SUITE_P(ReconcileAlignmentTest, ReconcileAlignmentTest,
testing::Values(std::make_tuple(4, 4, 4),
std::make_tuple(1, 4, 4),
std::make_tuple(8, 4, 8),
std::make_tuple(8, 3, 24)));
class CheckAlignmentTest
: public testing::TestWithParam<std::tuple<size_t, size_t, bool>> {};
TEST_P(CheckAlignmentTest, Test) {
ContainerT lhs, rhs, conflict;
SetAttr(&lhs, kTfLiteBufferAttrKeyAlignment, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeyAlignment, std::get<1>(GetParam()));
EXPECT_EQ(std::get<2>(GetParam()),
CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &conflict));
EXPECT_EQ(
!std::get<2>(GetParam()),
conflict.count(static_cast<uint32_t>(kTfLiteBufferAttrKeyAlignment)));
}
INSTANTIATE_TEST_SUITE_P(CheckAlignmentTest, CheckAlignmentTest,
testing::Values(std::make_tuple(4, 4, true),
std::make_tuple(4, 1, true),
std::make_tuple(1, 4, false)));
class ReconcilePaddingTest
: public testing::TestWithParam<std::tuple<size_t, size_t, size_t>> {};
TEST_P(ReconcilePaddingTest, Test) {
ContainerT lhs, rhs, merged;
SetAttr(&lhs, kTfLiteBufferAttrKeyPadding, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeyPadding, std::get<1>(GetParam()));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(std::get<2>(GetParam()),
GetAttr<size_t>(merged, kTfLiteBufferAttrKeyPadding));
}
INSTANTIATE_TEST_SUITE_P(ReconcilePaddingTest, ReconcilePaddingTest,
testing::Values(std::make_tuple(4, 4, 4),
std::make_tuple(1, 4, 4),
std::make_tuple(8, 4, 8),
std::make_tuple(8, 3, 24)));
class CheckPaddingTest
: public testing::TestWithParam<std::tuple<size_t, size_t, bool>> {};
TEST_P(CheckPaddingTest, Test) {
ContainerT lhs, rhs, conflict;
SetAttr(&lhs, kTfLiteBufferAttrKeyPadding, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeyPadding, std::get<1>(GetParam()));
EXPECT_EQ(std::get<2>(GetParam()),
CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &conflict));
EXPECT_EQ(!std::get<2>(GetParam()),
conflict.count(static_cast<uint32_t>(kTfLiteBufferAttrKeyPadding)));
}
INSTANTIATE_TEST_SUITE_P(CheckPaddingTest, CheckPaddingTest,
testing::Values(std::make_tuple(4, 4, true),
std::make_tuple(4, 1, true),
std::make_tuple(1, 4, false)));
class ReconcileSizeTest
: public testing::TestWithParam<std::tuple<size_t, size_t, size_t>> {};
TEST_P(ReconcileSizeTest, Test) {
ContainerT lhs, rhs, merged;
SetAttr(&lhs, kTfLiteBufferAttrKeySize, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeySize, std::get<1>(GetParam()));
EXPECT_TRUE(ReconcileGeneralAttributeKeys(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(std::get<2>(GetParam()),
GetAttr<size_t>(merged, kTfLiteBufferAttrKeySize));
}
INSTANTIATE_TEST_SUITE_P(ReconcileSizeTest, ReconcileSizeTest,
testing::Values(std::make_tuple(4, 4, 4),
std::make_tuple(1, 4, 4),
std::make_tuple(8, 4, 8),
std::make_tuple(8, 3, 8)));
class CheckSizeTest
: public testing::TestWithParam<std::tuple<size_t, size_t, bool>> {};
TEST_P(CheckSizeTest, Test) {
ContainerT lhs, rhs, conflict;
SetAttr(&lhs, kTfLiteBufferAttrKeySize, std::get<0>(GetParam()));
SetAttr(&rhs, kTfLiteBufferAttrKeySize, std::get<1>(GetParam()));
EXPECT_EQ(std::get<2>(GetParam()),
CheckGeneralAttributeKeysCoverage(kTfLiteAttrMapTypeBuffer, &lhs,
&rhs, &conflict));
EXPECT_EQ(!std::get<2>(GetParam()),
conflict.count(static_cast<uint32_t>(kTfLiteBufferAttrKeySize)));
}
INSTANTIATE_TEST_SUITE_P(CheckSizeTest, CheckSizeTest,
testing::Values(std::make_tuple(4, 4, true),
std::make_tuple(4, 1, true),
std::make_tuple(1, 4, false)));
class ReconcileNameTest
: public testing::TestWithParam<std::tuple<TfLiteAttrMapType, uint32_t>> {};
TEST_P(ReconcileNameTest, Test) {
constexpr char name_string1[] = "string1";
std::string name_string1_1 = "string1";
constexpr char name_string2[] = "string2";
{
ContainerT lhs, rhs, merged;
SetAttr(&lhs, std::get<1>(GetParam()), name_string1);
SetAttr(&rhs, std::get<1>(GetParam()), name_string1_1.c_str());
EXPECT_TRUE(ReconcileGeneralAttributeKeys(std::get<0>(GetParam()), &lhs,
&rhs, &merged, nullptr));
EXPECT_EQ(0, strcmp(GetAttr<const char*>(merged, std::get<1>(GetParam())),
name_string1));
}
{
ContainerT lhs, rhs, merged, conflict;
SetAttr(&lhs, std::get<1>(GetParam()), name_string1);
SetAttr(&rhs, std::get<1>(GetParam()), name_string2);
EXPECT_FALSE(ReconcileGeneralAttributeKeys(std::get<0>(GetParam()), &lhs,
&rhs, &merged, &conflict));
EXPECT_TRUE(conflict.count(std::get<1>(GetParam())));
}
}
INSTANTIATE_TEST_SUITE_P(
ReconcileNameTest, ReconcileNameTest,
testing::Values(
std::make_tuple(
kTfLiteAttrMapTypeBuffer,
static_cast<uint32_t>(kTfLiteBufferAttrKeyResourceTypeName)),
std::make_tuple(kTfLiteAttrMapTypeSync,
static_cast<uint32_t>(
kTfLiteSynchronizationAttrKeyObjectTypeName))));
class CheckNameTest
: public testing::TestWithParam<std::tuple<TfLiteAttrMapType, uint32_t>> {};
TEST_P(CheckNameTest, Test) {
constexpr char name_string1[] = "string1";
std::string name_string1_1 = "string1";
constexpr char name_string2[] = "string2";
{
ContainerT lhs, rhs;
SetAttr(&lhs, std::get<1>(GetParam()), name_string1);
SetAttr(&rhs, std::get<1>(GetParam()), name_string1_1.c_str());
EXPECT_TRUE(CheckGeneralAttributeKeysCoverage(std::get<0>(GetParam()), &lhs,
&rhs, nullptr));
}
{
ContainerT lhs, rhs, conflict;
SetAttr(&lhs, std::get<1>(GetParam()), name_string1);
SetAttr(&rhs, std::get<1>(GetParam()), name_string2);
EXPECT_FALSE(CheckGeneralAttributeKeysCoverage(std::get<0>(GetParam()),
&lhs, &rhs, &conflict));
EXPECT_TRUE(conflict.count(std::get<1>(GetParam())));
}
}
INSTANTIATE_TEST_SUITE_P(
CheckNameTest, CheckNameTest,
testing::Values(
std::make_tuple(
kTfLiteAttrMapTypeBuffer,
static_cast<uint32_t>(kTfLiteBufferAttrKeyResourceTypeName)),
std::make_tuple(kTfLiteAttrMapTypeSync,
static_cast<uint32_t>(
kTfLiteSynchronizationAttrKeyObjectTypeName))));
} // namespace
} // namespace tflite::interop
@@ -0,0 +1,49 @@
/* 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/core/async/interop/variant.h"
#include <cstring>
namespace tflite {
namespace interop {
Variant::Variant() {
type = kInvalid;
val.i = 0;
}
bool Variant::operator==(const Variant& other) const {
if (type != other.type) return false;
switch (type) {
case kInvalid:
// Treats uninitialized variant equals.
return true;
case kInt:
return val.i == other.val.i;
case kSizeT:
return val.s == other.val.s;
case kString:
return (val.c == other.val.c) || (strcmp(val.c, other.val.c) == 0);
case kBool:
return val.b == other.val.b;
}
}
bool Variant::operator!=(const Variant& other) const {
return !(*this == other);
}
} // namespace interop
} // namespace tflite
@@ -0,0 +1,133 @@
/* 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_CORE_ASYNC_INTEROP_VARIANT_H_
#define TENSORFLOW_LITE_CORE_ASYNC_INTEROP_VARIANT_H_
#include <cstddef>
#include <string>
#include <utility>
namespace tflite {
namespace interop {
// Tagged union implementation for variant type.
// Getters and Setters have compile time check to ensure the type is supported
// in the variant. But this class won't perform runtime type check. Callers
// are required to ensure type used in getters are the same as setters.
// For pointer type values hold in the variant (including C-style string literal
// const char*), Variant does not hold the ownership of the value.
struct Variant {
Variant();
template <typename T>
explicit Variant(T v) {
Set(v);
}
template <typename T>
Variant& operator=(T v) {
Set(v);
return *this;
}
// Getter. Disabled if the type is not supported in the variant.
// Returns nullptr if requested type doesn't match the actual value type.
template <typename T>
const T* Get() const = delete;
// Setter. Disabled if the type is not supported in the variant.
template <typename T>
void Set(T v) = delete;
// Returns the opaque data pointer.
// Callers are responsible for ensuring to cast to correct type.
void const* GetPtr() const { return &val; }
// Comparator.
// If the underlying data is string type (const char*), performs a string
// comparison. Otherwise checks equality of the data.
bool operator==(const Variant& other) const;
bool operator!=(const Variant& other) const;
// Data types supported in the variant.
union {
int i;
size_t s;
const char* c;
bool b;
} val;
// Tracking bit used for equality comparison.
enum { kInvalid, kInt, kSizeT, kString, kBool } type;
};
// Copyable.
template <>
inline Variant::Variant(const Variant& v) : val(v.val), type(v.type) {}
// Copy assign with copy-and-swap.
template <>
inline Variant& Variant::operator=(Variant v) {
std::swap(val, v.val);
std::swap(type, v.type);
return *this;
}
// Accessor specializations.
template <>
inline const int* Variant::Get<int>() const {
if (type != kInt) return nullptr;
return &val.i;
}
template <>
inline const size_t* Variant::Get<size_t>() const {
if (type != kSizeT) return nullptr;
return &val.s;
}
template <>
inline const char* const* Variant::Get<const char*>() const {
if (type != kString) return nullptr;
return &val.c;
}
template <>
inline const bool* Variant::Get<bool>() const {
if (type != kBool) return nullptr;
return &val.b;
}
template <>
inline void Variant::Set(int v) {
val.i = v;
type = kInt;
}
template <>
inline void Variant::Set(size_t v) {
val.s = v;
type = kSizeT;
}
template <>
inline void Variant::Set(const char* v) {
val.c = v;
type = kString;
}
template <>
inline void Variant::Set(bool v) {
val.b = v;
type = kBool;
}
} // namespace interop
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_INTEROP_VARIANT_H_
@@ -0,0 +1,127 @@
/* 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/core/async/interop/variant.h"
#include <cstddef>
#include <string>
#include <gtest/gtest.h>
namespace tflite::interop {
namespace {
TEST(VariantTest, IntTest) {
{
Variant a(1);
EXPECT_EQ(1, *a.Get<int>());
}
{
Variant a(1);
a.Set(2);
EXPECT_EQ(2, *a.Get<int>());
}
{
Variant a(42);
Variant b(a);
EXPECT_EQ(42, *b.Get<int>());
}
{
Variant a(42);
EXPECT_EQ(42, *static_cast<const int*>(a.GetPtr()));
}
{
Variant a(42);
Variant b(42);
EXPECT_EQ(a, b);
b.Set(21);
EXPECT_NE(a, b);
}
}
TEST(VariantTest, SizeTTest) {
{
size_t v = 1;
Variant a(v);
EXPECT_EQ(1, *a.Get<size_t>());
}
{
size_t v = 1;
Variant a(v);
size_t t = 2;
a.Set(t);
EXPECT_EQ(2, *a.Get<size_t>());
}
{
size_t v = 42;
Variant a(v);
Variant b(a);
EXPECT_EQ(42, *b.Get<size_t>());
}
{
size_t v = 42;
Variant a(v);
EXPECT_EQ(42, *static_cast<const size_t*>(a.GetPtr()));
}
{
Variant a(size_t(42));
Variant b(size_t(42));
EXPECT_EQ(a, b);
b.Set(size_t(21));
EXPECT_NE(a, b);
}
}
TEST(VariantTest, StringTest) {
{
const char v[] = "string";
Variant a(v);
EXPECT_EQ(v, *a.Get<const char*>());
}
{
const char v[] = "string";
Variant a(v);
const char t[] = "another string";
a.Set(t);
EXPECT_EQ(t, *a.Get<const char*>());
}
{
const char v[] = "string";
Variant a(v);
Variant b(a);
EXPECT_EQ(v, *b.Get<const char*>());
}
{
const char v[] = "string";
Variant a(v);
EXPECT_EQ(v, *static_cast<const char* const*>(a.GetPtr()));
}
{
const char v[] = "string";
Variant a(v);
std::string str = "string";
Variant b(str.c_str());
EXPECT_EQ(a, b);
b.Set("another string");
EXPECT_NE(a, b);
}
}
TEST(VariantTest, TypeNotMatch) {
Variant a(1);
EXPECT_EQ(nullptr, a.Get<size_t>());
}
} // namespace
} // namespace tflite::interop
+117
View File
@@ -0,0 +1,117 @@
/* 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/core/async/task_internal.h"
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
namespace async {
bool ExecutionTask::GetTensorIdx(TfLiteIoType io_type, const char* name,
int* idx) const {
const std::map<std::string, uint32_t>* map = nullptr;
if (io_type == kTfLiteIoTypeInput) {
map = input_name_to_idx_;
} else {
map = output_name_to_idx_;
}
if (!map) return false;
if (auto it_idx = map->find(name); it_idx != map->end()) {
*idx = it_idx->second;
return true;
}
return false;
}
TfLiteBufferHandle ExecutionTask::GetBufferHandle(TfLiteIoType io_type,
const char* name) const {
int index = 0;
if (!GetTensorIdx(io_type, name, &index)) {
return kTfLiteNullBufferHandle;
}
return GetBufferHandle(index);
}
TfLiteBufferHandle ExecutionTask::GetBufferHandle(int tensor_index) const {
if (auto it = io_data_.find(tensor_index); it != io_data_.end()) {
return it->second.buf;
}
return kTfLiteNullBufferHandle;
}
TfLiteStatus ExecutionTask::SetBufferHandle(TfLiteIoType io_type,
const char* name,
TfLiteBufferHandle handle) {
int index = 0;
if (!GetTensorIdx(io_type, name, &index)) {
return kTfLiteError;
}
return SetBufferHandle(index, handle);
}
TfLiteStatus ExecutionTask::SetBufferHandle(int tensor_index,
TfLiteBufferHandle handle) {
io_data_[tensor_index].buf = handle;
return kTfLiteOk;
}
TfLiteSynchronization* ExecutionTask::GetSynchronization(
TfLiteIoType io_type, const char* name) const {
int index = 0;
if (!GetTensorIdx(io_type, name, &index)) {
return nullptr;
}
return GetSynchronization(index);
}
TfLiteSynchronization* ExecutionTask::GetSynchronization(
int tensor_index) const {
if (auto it = io_data_.find(tensor_index); it != io_data_.end()) {
return it->second.sync;
}
return nullptr;
}
TfLiteStatus ExecutionTask::SetSynchronization(TfLiteIoType io_type,
const char* name,
TfLiteSynchronization* sync) {
int index = 0;
if (!GetTensorIdx(io_type, name, &index)) {
return kTfLiteError;
}
return SetSynchronization(index, sync);
}
TfLiteStatus ExecutionTask::SetSynchronization(int tensor_index,
TfLiteSynchronization* sync) {
io_data_[tensor_index].sync = sync;
return kTfLiteOk;
}
} // namespace async
} // namespace tflite
TfLiteExecutionTask::TfLiteExecutionTask() {
task = std::make_unique<tflite::async::ExecutionTask>();
}
+161
View File
@@ -0,0 +1,161 @@
/* 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_CORE_ASYNC_TASK_INTERNAL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_TASK_INTERNAL_H_
#include <atomic>
#include <map>
#include <memory>
#include <string>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
// Forward declaration
namespace tflite::async {
class ExecutionTask;
} // namespace tflite::async
// TfLiteExecutionTask object holds the mapping from tensor index to
// the backend buffer and sync object tied to that tensor.
// It also holds the event handle that represents a specific scheduled
// async execution.
// The AsyncSignatureRunner that creates the task should out-live the life time
// of the TfLiteExecutionTask.
struct TfLiteExecutionTask {
TfLiteExecutionTask();
std::unique_ptr<tflite::async::ExecutionTask> task;
};
namespace tflite {
namespace async {
// Implementation of TfLiteExecutionTask.
// This class is not thread safe.
class ExecutionTask {
public:
// Returns the buffer handle for input / output tensor `name`.
// If there's tensor `name` is not found, returns kTfLiteNullBufferHandle.
TfLiteBufferHandle GetBufferHandle(TfLiteIoType io_type,
const char* name) const;
// Same as GetBufferHandle above, but uses tensor index as key.
TfLiteBufferHandle GetBufferHandle(int tensor_index) const;
// Sets the buffer handle for input / output `name`.
// If there's tensor `name` is not found, do nothing.
TfLiteStatus SetBufferHandle(TfLiteIoType io_type, const char* name,
TfLiteBufferHandle handle);
// Same as SetBufferHandle above but uses tensor index. Callers need to make
// sure the index is valid.
TfLiteStatus SetBufferHandle(int tensor_index, TfLiteBufferHandle handle);
// Returns the TfLiteSynchronization for input / output tensor `name`.
// If there's tensor `name` is not found, returns nullptr.
TfLiteSynchronization* GetSynchronization(TfLiteIoType io_type,
const char* name) const;
// Same as GetSynchronization above, but uses tensor index as key.
TfLiteSynchronization* GetSynchronization(int tensor_index) const;
// Sets the TfLiteSynchronization for input / output tensor `name`.
// If there's tensor `name` is not found, do nothing.
TfLiteStatus SetSynchronization(TfLiteIoType io_type, const char* name,
TfLiteSynchronization* sync);
// Same as TfLiteSynchronization above but uses tensor index. Callers need to
// make sure the index is valid.
TfLiteStatus SetSynchronization(int tensor_index,
TfLiteSynchronization* sync);
using TensorNameMapT = std::map<std::string, uint32_t>;
// Sets the mapping from signature input name to tensor index.
void SetInputNameMap(const TensorNameMapT* input_name_to_idx) {
input_name_to_idx_ = input_name_to_idx;
}
// Sets the mapping from signature output name to tensor index.
void SetOutputNameMap(const TensorNameMapT* output_name_to_idx) {
output_name_to_idx_ = output_name_to_idx;
}
// Returns the status of this task.
// True if the task has been scheduled, false if idle.
bool Scheduled() const { return scheduled_.load(); }
// Exchanges the status of this task. Whether it's been scheduled or in idle
// state.
// Returns the previous value of the task.
bool SetScheduled(bool scheduled) { return scheduled_.exchange(scheduled); }
// Returns the latest status of this task.
// Thread safe.
TfLiteStatus Status() const { return status_.load(); }
// Sets the status code for this task.
// Thread safe.
void SetStatus(TfLiteStatus status) { status_.store(status); }
// Sets the delegate execution data for this task.
void SetDelegateExecutionData(TfLiteAsyncKernel* kernel, void* data) {
data_ = data;
}
// Returns the delegate execution data for this task.
// Returns nullptr if not set.
void* GetDelegateExecutionData(TfLiteAsyncKernel* kernel) const {
return data_;
}
private:
struct IOData {
TfLiteBufferHandle buf = kTfLiteNullBufferHandle;
TfLiteSynchronization* sync = nullptr;
};
// Finds the tensor index for input / output name.
// Returns false if the tensor is not found.
bool GetTensorIdx(TfLiteIoType io_type, const char* name, int* idx) const;
// Mapping from tensor index to buffer handle and sync object ptr.
// Set by the application.
std::map<int, IOData> io_data_;
// The status of the task. Whether the task has been scheduled or not.
// The bit is set when calling InvokeAsync to this task, and resets on Wait.
std::atomic_bool scheduled_ = false;
// The latest status of this task. Default value will be kTfLiteOk.
std::atomic<TfLiteStatus> status_ = kTfLiteOk;
// Mapping from signature name to tensor index.
// Not owned. Set and owned by AsyncSignatureRunner.
// Can be nullptr if the model does not have signature def.
const TensorNameMapT* input_name_to_idx_ = nullptr;
const TensorNameMapT* output_name_to_idx_ = nullptr;
// Delegate owned data.
// NOTE: Currently we only support one delegate. If we are to support multiple
// backends, we might need to change this to a map.
void* data_ = nullptr;
};
} // namespace async
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_TASK_INTERNAL_H_
@@ -0,0 +1,103 @@
/* 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/core/async/task_internal.h"
#include <gtest/gtest.h>
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/c/common.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/async/interop/c/types.h"
namespace tflite::async {
TEST(TfLiteExecutionTaskTest, BasicTest) {
tflite::async::ExecutionTask task;
tflite::async::ExecutionTask::TensorNameMapT input_names;
input_names["x"] = 1;
input_names["y"] = 2;
tflite::async::ExecutionTask::TensorNameMapT output_names;
output_names["a"] = 3;
task.SetInputNameMap(&input_names);
task.SetOutputNameMap(&output_names);
auto* sync = TfLiteSynchronizationCreate();
EXPECT_EQ(kTfLiteOk, task.SetBufferHandle(kTfLiteIoTypeInput, "x", 42));
EXPECT_EQ(kTfLiteOk, task.SetBufferHandle(kTfLiteIoTypeInput, "y", 43));
EXPECT_EQ(kTfLiteOk, task.SetBufferHandle(kTfLiteIoTypeOutput, "a", 44));
EXPECT_EQ(kTfLiteOk, task.SetSynchronization(kTfLiteIoTypeInput, "x", sync));
EXPECT_EQ(42, task.GetBufferHandle(kTfLiteIoTypeInput, "x"));
EXPECT_EQ(43, task.GetBufferHandle(kTfLiteIoTypeInput, "y"));
EXPECT_EQ(44, task.GetBufferHandle(kTfLiteIoTypeOutput, "a"));
EXPECT_EQ(sync, task.GetSynchronization(kTfLiteIoTypeInput, "x"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeInput, "y"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeOutput, "a"));
TfLiteSynchronizationDelete(sync);
}
TEST(TfLiteExecutionTaskTest, NameMapUninitialized) {
tflite::async::ExecutionTask task;
EXPECT_EQ(kTfLiteNullBufferHandle,
task.GetBufferHandle(kTfLiteIoTypeInput, "foo"));
EXPECT_EQ(kTfLiteNullBufferHandle,
task.GetBufferHandle(kTfLiteIoTypeOutput, "foo"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeOutput, "foo"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeOutput, "foo"));
}
TEST(TfLiteExecutionTaskTest, NoMatchingName) {
tflite::async::ExecutionTask task;
tflite::async::ExecutionTask::TensorNameMapT input_names;
input_names["x"] = 1;
input_names["y"] = 2;
tflite::async::ExecutionTask::TensorNameMapT output_names;
output_names["a"] = 3;
task.SetInputNameMap(&input_names);
task.SetOutputNameMap(&output_names);
auto* sync = TfLiteSynchronizationCreate();
EXPECT_EQ(kTfLiteError, task.SetBufferHandle(kTfLiteIoTypeInput, "xx", 42));
EXPECT_EQ(kTfLiteError, task.SetBufferHandle(kTfLiteIoTypeOutput, "aa", 44));
EXPECT_EQ(kTfLiteError,
task.SetSynchronization(kTfLiteIoTypeInput, "xx", sync));
EXPECT_EQ(kTfLiteError,
task.SetSynchronization(kTfLiteIoTypeOutput, "aa", sync));
EXPECT_EQ(kTfLiteNullBufferHandle,
task.GetBufferHandle(kTfLiteIoTypeInput, "xx"));
EXPECT_EQ(kTfLiteNullBufferHandle,
task.GetBufferHandle(kTfLiteIoTypeOutput, "aa"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeInput, "xx"));
EXPECT_EQ(nullptr, task.GetSynchronization(kTfLiteIoTypeOutput, "aa"));
TfLiteSynchronizationDelete(sync);
}
TEST(TfLiteExecutionTaskTest, DelegateData) {
TfLiteAsyncKernel kernel{};
int data = 0;
tflite::async::ExecutionTask task;
EXPECT_EQ(nullptr, task.GetDelegateExecutionData(&kernel));
task.SetDelegateExecutionData(&kernel, &data);
EXPECT_EQ(&data, task.GetDelegateExecutionData(&kernel));
}
} // namespace tflite::async
+38
View File
@@ -0,0 +1,38 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
# Test utilities for TFLite async execution.
load("//tensorflow/lite/core/shims:cc_library_with_tflite.bzl", "cc_library_with_tflite")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "test_backend",
testonly = 1,
srcs = ["test_backend.cc"],
hdrs = ["test_backend.h"],
deps = [
"//tensorflow/lite:array",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite/core/async:async_kernel_internal",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
"//tensorflow/lite/delegates:utils",
],
)
cc_library_with_tflite(
name = "mock_async_kernel",
testonly = 1,
hdrs = ["mock_async_kernel.h"],
deprecation = "Use //tensorflow/lite/async/testing:mock_async_kernel instead.",
tflite_deps = [
"//tensorflow/lite/async/testing:mock_async_kernel",
],
)
@@ -0,0 +1,21 @@
/* 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_CORE_ASYNC_TESTING_MOCK_ASYNC_KERNEL_H_
#define TENSORFLOW_LITE_CORE_ASYNC_TESTING_MOCK_ASYNC_KERNEL_H_
#include "tensorflow/lite/async/testing/mock_async_kernel.h" // IWYU pragma: export
// IWYU pragma: private, include "third_party/tensorflow/lite/async/testing/mock_async_kernel.h"
#endif // TENSORFLOW_LITE_CORE_ASYNC_TESTING_MOCK_ASYNC_KERNEL_H_
@@ -0,0 +1,94 @@
/* 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/core/async/testing/test_backend.h"
#include <cstddef>
#include <string>
#include "tensorflow/lite/array.h"
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/delegates/utils.h"
namespace tflite {
namespace async {
namespace testing {
namespace {
TfLiteStatus DelegatePrepare(TfLiteContext* context,
TfLiteDelegate* tflite_delegate) {
auto* backend = reinterpret_cast<TestBackend*>(tflite_delegate->data_);
// Can delegate all nodes.
delegates::IsNodeSupportedFn node_supported_fn =
[=](TfLiteContext* context, TfLiteNode* node,
TfLiteRegistration* registration,
std::string* unsupported_details) -> bool { return true; };
delegates::GraphPartitionHelper helper(context, node_supported_fn);
TF_LITE_ENSURE_STATUS(helper.Partition(nullptr));
auto supported_nodes = helper.GetNodesOfFirstNLargestPartitions(
backend->NumPartitions(), backend->MinPartitionedNodes());
// Create TfLiteRegistration with the provided async kernel.
TfLiteRegistration reg{};
reg.init = [](TfLiteContext* context, const char* buffer,
size_t length) -> void* {
const TfLiteDelegateParams* params =
reinterpret_cast<const TfLiteDelegateParams*>(buffer);
auto* backend = reinterpret_cast<TestBackend*>(params->delegate->data_);
// AsyncSubgraph requires TfLiteNode.user_data to be of TfLiteAsyncKernel
// type.
return backend->get_kernel();
};
reg.free = [](TfLiteContext*, void*) -> void {};
reg.prepare = [](TfLiteContext*, TfLiteNode*) -> TfLiteStatus {
return kTfLiteOk;
};
reg.invoke = [](TfLiteContext*, TfLiteNode*) -> TfLiteStatus {
return kTfLiteOk;
};
reg.profiling_string = nullptr;
reg.builtin_code = kTfLiteBuiltinDelegate;
reg.custom_name = "TestBackend";
reg.version = 1;
reg.async_kernel = [](TfLiteContext*,
TfLiteNode* node) -> TfLiteAsyncKernel* {
return reinterpret_cast<TfLiteAsyncKernel*>(node->user_data);
};
return context->ReplaceNodeSubsetsWithDelegateKernels(
context, reg, BuildTfLiteArray(supported_nodes).get(), tflite_delegate);
}
} // namespace
TestBackend::TestBackend(TfLiteAsyncKernel* kernel)
: kernel_(kernel), delegate_(TfLiteDelegateCreate()) {
delegate_.Prepare = &DelegatePrepare;
delegate_.CopyFromBufferHandle = nullptr;
delegate_.CopyToBufferHandle = nullptr;
delegate_.FreeBufferHandle = nullptr;
delegate_.data_ = this;
}
} // namespace testing
} // namespace async
} // namespace tflite
@@ -0,0 +1,64 @@
/* 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_CORE_ASYNC_TESTING_TEST_BACKEND_H_
#define TENSORFLOW_LITE_CORE_ASYNC_TESTING_TEST_BACKEND_H_
#include <limits>
#include <memory>
#include "tensorflow/lite/core/async/async_kernel_internal.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/c/common.h"
namespace tflite {
namespace async {
namespace testing {
// A test backend that takes in arbitrary TfLiteAsyncKernel.
class TestBackend {
public:
explicit TestBackend(TfLiteAsyncKernel* kernel);
TfLiteDelegate* get_delegate() { return &delegate_; }
TfLiteAsyncKernel* get_kernel() { return kernel_; }
// Maximum delegate partitions.
int NumPartitions() const { return num_partitions_; }
void SetNumPartitions(int num_partitions) {
num_partitions_ = num_partitions;
}
// Minimal number of nodes the backend delegates.
int MinPartitionedNodes() const { return min_partioned_nodes_; }
void SetMinPartitionedNodes(int min_partioned_nodes) {
min_partioned_nodes_ = min_partioned_nodes;
}
private:
// Not owned.
TfLiteAsyncKernel* kernel_ = nullptr;
// Owned.
TfLiteDelegate delegate_;
int num_partitions_ = std::numeric_limits<int>::max();
int min_partioned_nodes_ = 0;
};
} // namespace testing
} // namespace async
} // namespace tflite
#endif // TENSORFLOW_LITE_CORE_ASYNC_TESTING_TEST_BACKEND_H_
+670
View File
@@ -0,0 +1,670 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_cc//cc:cc_test.bzl", "cc_test")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load(
"//tensorflow/lite:build_def.bzl",
"tflite_cc_library_with_c_headers_test",
"tflite_copts",
)
load(
"//tensorflow/lite/core/c:special_rules.bzl",
"c_api_experimental_visibility_allowlist",
"c_api_visibility_allowlist",
"common_header_visibility_allowlist",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
# "//litert:__subpackages__", # copybara:uncomment
# copybara:uncomment "//third_party/odml/litert:__subpackages__",
"//tensorflow/lite:__subpackages__",
],
licenses = ["notice"],
)
exports_files(
srcs = [
"builtin_op_data.h",
"c_api.h",
"c_api_experimental.h",
"c_api_opaque.h",
"c_api_types.h",
"common.h",
"operator.h",
],
visibility = [
"//tensorflow/lite:__subpackages__",
],
)
bzl_library(
name = "special_rules_bzl",
srcs = ["special_rules.bzl"],
visibility = ["//tensorflow/lite:__subpackages__"],
)
filegroup(
name = "headers_filegroup",
srcs = [
"builtin_op_data.h",
"c_api.h",
"c_api_types.h",
"common.h",
"operator.h",
"//tensorflow/compiler/mlir/lite/core/c:lite_headers_filegroup",
"//tensorflow/lite/core/async/c:types.h",
],
visibility = [
# Temporary workaround to make visible to litert in OSS (default vis is not transformed correctly.)
"//visibility:public",
],
)
filegroup(
name = "tflite_internal_cc_3p_api_deps_src",
srcs = [
"common.cc",
"common.h",
],
visibility = [
"//tensorflow/lite:__pkg__",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api",
hdrs = [
"c_api.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + c_api_visibility_allowlist(),
deps = [
":c_api_types",
":c_api_without_op_resolver",
":operator",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:create_op_resolver_with_builtin_ops",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core/async/c:types",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api_without_op_resolver",
srcs = ["c_api.cc"],
hdrs = ["c_api.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
visibility = [
"//tensorflow/lite:__subpackages__",
],
deps = [
":c_api_types",
":operator",
"//tensorflow/compiler/mlir/lite:allocation",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:version",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core:create_op_resolver_header",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core:signature_runner",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/delegates:interpreter_utils",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/kernels/internal:compatibility",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/schema:schema_fbs",
],
alwayslink = 1, # Why?? TODO(b/161243354): eliminate this.
)
tflite_cc_library_with_c_headers_test(
name = "private_c_api_without_op_resolver",
hdrs = ["c_api.h"],
copts = tflite_copts(),
tags = [
"allow_undefined_symbols", # For tflite::CreateOpResolver().
"avoid_dep",
],
visibility = [
"//visibility:public",
],
deps = [
":c_api_types",
":operator",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite/core/async/c:types",
],
)
# Same as ":c_api_without_op_resolver", but without alwayslink=1.
tflite_cc_library_with_c_headers_test(
name = "c_api_without_op_resolver_without_alwayslink",
srcs = ["c_api.cc"],
hdrs = ["c_api.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
deps = [
":c_api_types",
":operator_without_alwayslink",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:mutable_op_resolver",
"//tensorflow/lite:stderr_reporter",
"//tensorflow/lite:version",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core:create_op_resolver_header",
"//tensorflow/lite/core:framework_stable",
"//tensorflow/lite/core:signature_runner",
"//tensorflow/lite/core/api",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/delegates:interpreter_utils",
"//tensorflow/lite/delegates/nnapi:nnapi_delegate",
"//tensorflow/lite/kernels:kernel_util",
"//tensorflow/lite/kernels/internal:compatibility",
"//tensorflow/lite/profiling/telemetry:profiler",
"//tensorflow/lite/schema:schema_fbs",
],
)
# This is a private target, its visibility is set to public only to be
# used by "tflite_custom_c_library".
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_without_op_resolver_without_alwayslink",
actual = ":c_api_without_op_resolver_without_alwayslink",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
cc_test(
name = "c_api_test",
size = "small",
srcs = ["c_api_test.cc"],
copts = tflite_copts(),
data = [
"//tensorflow/lite:testdata/2_subgraphs.bin",
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/add_quantized.bin",
"//tensorflow/lite:testdata/custom_sinh.bin",
],
deps = [
":c_api",
":c_api_experimental",
":c_api_types",
":common",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:string_util",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/delegates:delegate_test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/testing:util",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "c_api_test_with_opaque_delegate",
size = "small",
srcs = ["c_api_test.cc"],
copts = tflite_copts(),
data = [
"//tensorflow/lite:testdata/2_subgraphs.bin",
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/add_quantized.bin",
"//tensorflow/lite:testdata/custom_sinh.bin",
],
local_defines = ["TFLITE_USE_OPAQUE_DELEGATE"],
deps = [
":c_api",
":c_api_experimental",
":c_api_types",
":common",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:string_util",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/delegates:delegate_test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/testing:util",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "selectively_built_c_api_test",
size = "small",
srcs = ["c_api_test.cc"],
copts = tflite_copts(),
data = [
"//tensorflow/lite:testdata/2_subgraphs.bin",
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/add_quantized.bin",
"//tensorflow/lite:testdata/custom_sinh.bin",
],
deps = [
":c_api_experimental_without_op_resolver_without_alwayslink",
":c_api_types",
":c_api_without_op_resolver_without_alwayslink",
":common",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:string_util",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/c:selectively_built_c_api_test_lib",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/delegates:delegate_test_util",
"//tensorflow/lite/schema:schema_fbs",
"//tensorflow/lite/testing:util",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "builtin_op_data_test",
size = "small",
srcs = ["builtin_op_data_test.cc"],
copts = ["-Wno-unused-variable"],
deps = [
":common",
"@com_google_googletest//:gtest_main",
],
)
# This is a private target, its visibility is set to public only to be
# used by "tflite_custom_c_library" and LiteRT dependencies.
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_types",
actual = ":c_api_types",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api_types",
hdrs = ["c_api_types.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
# "//litert/litert:__subpackages__", # copybara:uncomment
# copybara:uncomment "//third_party/odml/litert/litert:__subpackages__",
"//tensorflow/compiler/mlir/lite/experimental/lrt:__subpackages__",
"//tensorflow/lite:__subpackages__",
] + c_api_visibility_allowlist(),
deps = [
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
],
)
# Test the C extension API code.
cc_test(
name = "common_test",
size = "small",
srcs = ["common_test.cc"],
deps = [
":c_api_types",
":common",
"//tensorflow/lite:array",
"//tensorflow/lite:test_util",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_types",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "c_api_opaque_test",
size = "small",
srcs = [
"c_api_opaque.cc",
"c_api_opaque.h",
"c_api_opaque_test.cc",
],
data = [
"//tensorflow/lite:testdata/custom_sinh.bin",
"//tensorflow/lite:testdata/with_metadata.bin",
],
deps = [
":c_api",
":c_api_types",
":common",
":operator",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:string_util",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_opaque_internal",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/c:common",
"//tensorflow/lite/core:subgraph",
"//tensorflow/lite/kernels:kernel_util",
"@com_google_googletest//:gtest_main",
],
)
tflite_cc_library_with_c_headers_test(
name = "common",
srcs = ["common.cc"],
hdrs = [
"builtin_op_data.h",
"common.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = ["//tensorflow/lite:__subpackages__"] + common_header_visibility_allowlist(),
deps = [
":c_api_types",
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
"//tensorflow/lite:tflite_kernel_use_xnnpack_optional",
] + select({
"//tensorflow/lite:tensorflow_profiler_config": [
"//tensorflow/lite:macros",
"//tensorflow/lite:tensorflow_profiler_logger_shim",
],
"//conditions:default": [],
}),
alwayslink = 1, # Why?? TODO(b/161243354): eliminate this.
)
# This is a private target, its visibility is set to public only to be
# used by "tflite_custom_c_library" and "tflite_flex_cc_library".
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_common",
actual = ":common",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api_experimental",
hdrs = [
"c_api_experimental.h",
# DEPRECATED
# ':c_api_opaque' was promoted to the 'mostly stable' API.
# Please explicitly add a dependency on ':c_api_opaque' if your target
# needs both ':c_api_experimental' and ':c_api_opaque' dependencies.
# We plan to remove 'c_api_opaque.h' from the list of exposed headers and
# will remove the dependency from this target on ':c_api_opaque' in the future.
"c_api_opaque.h",
],
copts = tflite_copts(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + c_api_experimental_visibility_allowlist(),
deps = [
":c_api",
":c_api_experimental_without_op_resolver",
":c_api_opaque",
":c_api_types",
":common",
":operator",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:signature_runner",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/core:framework",
],
)
# Same as ":c_api_experimental", but without linking in the default CreateOpResolver implementation.
tflite_cc_library_with_c_headers_test(
name = "c_api_experimental_without_op_resolver",
srcs = [
"c_api_experimental.cc",
],
hdrs = [
"c_api_experimental.h",
# DEPRECATED
# ':c_api_opaque_without_op_resolver' was promoted to the 'mostly stable' API.
# Please explicitly add a dependency on ':c_api_opaque_without_op_resolver' if your target
# needs both ':c_api_experimental_without_op_resolver' and ':c_api_opaque_without_op_resolver' dependencies.
# We plan to remove 'c_api_opaque.h' from the list of exposed headers and
# will remove the dependency from this target on ':c_api_opaque_without_op_resolver' in the future.
"c_api_opaque.h",
],
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
deps = [
":c_api_opaque_without_op_resolver",
":c_api_types",
":c_api_without_op_resolver",
":common",
":operator",
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite:framework",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/core:framework",
"//tensorflow/lite/profiling/telemetry:profiler",
],
alwayslink = 1, # Why?? TODO(b/161243354): eliminate this.
)
# This is a private target, its visibility is set to public only to be
# used by "custom_c_library_with_tflite".
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_experimental_without_op_resolver",
actual = ":c_api_experimental_without_op_resolver",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
# Same as ":c_api_experimental", but without linking in the default CreateOpResolver implementation.
tflite_cc_library_with_c_headers_test(
name = "c_api_experimental_without_op_resolver_without_alwayslink",
srcs = [
"c_api_experimental.cc",
],
hdrs = [
"c_api_experimental.h",
# DEPRECATED
# ':c_api_opaque_without_op_resolver_without_alwayslink' was promoted to the 'mostly stable' API.
# Please explicitly add a dependency on ':c_api_opaque_without_op_resolver_without_alwayslink'
# if your target needs both ':c_api_experimental_without_op_resolver_without_alwayslink' and
# ':c_api_opaque_without_op_resolver_without_alwayslink' dependencies.
# We plan to remove 'c_api_opaque.h' from the list of exposed headers and
# will remove the dependency from this target on
# ':c_api_opaque_without_op_resolver_without_alwayslink' in the future.
"c_api_opaque.h",
],
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
deps = [
":c_api_opaque_without_op_resolver_without_alwayslink",
":c_api_types",
":c_api_without_op_resolver_without_alwayslink",
":common",
":operator_without_alwayslink",
"//tensorflow/lite:framework",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite/c:c_api_internal",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/core:framework",
"//tensorflow/lite/profiling/telemetry:profiler",
],
)
# This is a private target, its visibility is set to public only to be
# used by "tflite_custom_c_library".
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_experimental_without_op_resolver_without_alwayslink",
actual = ":c_api_experimental_without_op_resolver_without_alwayslink",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api_opaque",
hdrs = [
"c_api_opaque.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + c_api_experimental_visibility_allowlist(),
deps = [
":c_api",
":c_api_opaque_without_op_resolver",
":c_api_types",
":common",
":operator",
"//tensorflow/lite/core:framework",
],
)
tflite_cc_library_with_c_headers_test(
name = "c_api_opaque_without_op_resolver",
srcs = [
"c_api_opaque.cc",
],
hdrs = [
"c_api_opaque.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
deps = [
":c_api_types",
":c_api_without_op_resolver",
":common",
":operator",
"//tensorflow/lite:string_util",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_opaque_internal",
"//tensorflow/lite/core:framework",
"//tensorflow/lite/kernels:kernel_util",
],
)
# This is a private target, its visibility is set to public only to be
# used by "custom_c_library_with_tflite" and LiteRT dependencies.
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_opaque_without_op_resolver",
actual = ":c_api_opaque_without_op_resolver",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
# Same as ":c_api_opaque_without_op_resolver", but without alwayslink=1.
tflite_cc_library_with_c_headers_test(
name = "c_api_opaque_without_op_resolver_without_alwayslink",
srcs = [
"c_api_opaque.cc",
],
hdrs = [
"c_api_opaque.h",
],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
tags = ["allow_undefined_symbols"], # For tflite::CreateOpResolver().
deps = [
":c_api_types",
":c_api_without_op_resolver_without_alwayslink",
":common",
":operator_without_alwayslink",
"//tensorflow/lite:string_util",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_opaque_internal_without_alwayslink",
"//tensorflow/lite/core:framework",
"//tensorflow/lite/kernels:kernel_util",
],
)
# This is a private target, its visibility is set to public only to be
# used by "tflite_custom_c_library".
# Do not use this target directly and don't consider it as a part of the public API.
alias(
name = "private_c_api_opaque_without_op_resolver_without_alwayslink",
actual = ":c_api_opaque_without_op_resolver_without_alwayslink",
tags = ["avoid_dep"],
visibility = [
"//visibility:public",
],
)
cc_test(
name = "c_api_experimental_test",
size = "small",
srcs = ["c_api_experimental_test.cc"],
copts = tflite_copts(),
data = [
"//tensorflow/lite:testdata/add.bin",
"//tensorflow/lite:testdata/custom_sinh.bin",
],
deps = [
":c_api",
":c_api_experimental",
":common",
"//tensorflow/lite:kernel_api",
"//tensorflow/lite:util",
"//tensorflow/lite/c:c_api_types",
"//tensorflow/lite/delegates:delegate_test_util",
"//tensorflow/lite/testing:util",
"@com_google_absl//absl/strings:string_view",
"@com_google_googletest//:gtest_main",
],
)
tflite_cc_library_with_c_headers_test(
name = "operator",
srcs = ["operator.cc"],
hdrs = ["operator.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
visibility = [
"//tensorflow/lite:__subpackages__",
] + c_api_visibility_allowlist(),
deps = [
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite/c:common",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
# When building shared libraries, we need alwayslink here, since the functions
# in this header are part of the public API.
alwayslink = 1,
)
tflite_cc_library_with_c_headers_test(
name = "operator_without_alwayslink",
srcs = ["operator.cc"],
hdrs = ["operator.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
deps = [
"//tensorflow/lite:builtin_ops",
"//tensorflow/lite/c:common",
"//tensorflow/lite/c:common_internal",
"//tensorflow/lite/core/async/c:types",
"//tensorflow/lite/core/c:c_api_types",
"//tensorflow/lite/core/c:common",
],
)
+26
View File
@@ -0,0 +1,26 @@
/* 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.
==============================================================================*/
/// WARNING: Users of TensorFlow Lite should not include this file directly,
/// but should instead include
/// "third_party/tensorflow/lite/c/builtin_op_data.h".
/// Only the TensorFlow Lite implementation itself should include this
/// file directly.
#ifndef TENSORFLOW_LITE_CORE_C_BUILTIN_OP_DATA_H_
#define TENSORFLOW_LITE_CORE_C_BUILTIN_OP_DATA_H_
#include "tensorflow/compiler/mlir/lite/core/c/builtin_op_data.h" // IWYU pragma: export
#include "tensorflow/lite/core/c/common.h" // IWYU pragma: export
#endif // TENSORFLOW_LITE_CORE_C_BUILTIN_OP_DATA_H_
@@ -0,0 +1,85 @@
/* 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/core/c/builtin_op_data.h"
#include <gtest/gtest.h>
namespace tflite {
// Builtin op data is just a set of data definitions, so the only meaningful
// test we can run is whether we can create the structs we expect to find.
// Testing each struct's members might be possible, but it seems unnecessary
// until we've locked down the API. The build rule has copts set to ignore the
// unused variable warning, since this is just a compilation test.
TEST(IntArray, CanCompileStructs) {
TfLitePadding padding = kTfLitePaddingSame;
TfLitePaddingValues padding_values;
TfLiteFusedActivation fused_activation = kTfLiteActRelu;
TfLiteConvParams conv_params;
TfLitePoolParams pool_params;
TfLiteDepthwiseConvParams depthwise_conv_params;
TfLiteSVDFParams svdf_params;
TfLiteRNNParams rnn_params;
TfLiteSequenceRNNParams sequence_rnn_params;
TfLiteFullyConnectedWeightsFormat fully_connected_weights_format =
kTfLiteFullyConnectedWeightsFormatDefault;
TfLiteFullyConnectedParams fully_connected_params;
TfLiteLSHProjectionType projection_type = kTfLiteLshProjectionDense;
TfLiteLSHProjectionParams projection_params;
TfLiteSoftmaxParams softmax_params;
TfLiteConcatenationParams concatenation_params;
TfLiteAddParams add_params;
TfLiteSpaceToBatchNDParams space_to_batch_nd_params;
TfLiteBatchToSpaceNDParams batch_to_space_nd_params;
TfLiteMulParams mul_params;
TfLiteSubParams sub_params;
TfLiteDivParams div_params;
TfLiteL2NormParams l2_norm_params;
TfLiteLocalResponseNormParams local_response_norm_params;
TfLiteLSTMKernelType lstm_kernel_type = kTfLiteLSTMBasicKernel;
TfLiteLSTMParams lstm_params;
TfLiteResizeBilinearParams resize_bilinear_params;
TfLitePadParams pad_params;
TfLitePadV2Params pad_v2_params;
TfLiteReshapeParams reshape_params;
TfLiteSkipGramParams skip_gram_params;
TfLiteSpaceToDepthParams space_to_depth_params;
TfLiteDepthToSpaceParams depth_to_space_params;
TfLiteCastParams cast_params;
TfLiteCombinerType combiner_type = kTfLiteCombinerTypeSqrtn;
TfLiteEmbeddingLookupSparseParams lookup_sparse_params;
TfLiteGatherParams gather_params;
TfLiteTransposeParams transpose_params;
TfLiteReducerParams reducer_params;
TfLiteSplitParams split_params;
TfLiteSplitVParams split_v_params;
TfLiteSqueezeParams squeeze_params;
TfLiteStridedSliceParams strided_slice_params;
TfLiteArgMaxParams arg_max_params;
TfLiteArgMinParams arg_min_params;
TfLiteTransposeConvParams transpose_conv_params;
TfLiteSparseToDenseParams sparse_to_dense_params;
TfLiteShapeParams shape_params;
TfLiteRankParams rank_params;
TfLiteFakeQuantParams fake_quant_params;
TfLitePackParams pack_params;
TfLiteUnpackParams unpack_params;
TfLiteOneHotParams one_hot_params;
TfLiteBidirectionalSequenceRNNParams bidi_sequence_rnn_params;
TfLiteBidirectionalSequenceLSTMParams bidi_sequence_lstm_params;
}
} // namespace tflite
+622
View File
@@ -0,0 +1,622 @@
/* 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/core/c/c_api.h"
#include <stddef.h>
#include <string.h>
#include <cstdarg>
#include <cstdint>
#include <memory>
#include <mutex> // NOLINT
#include <utility>
#include <vector>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/c_api_internal.h"
#include "tensorflow/lite/c/common_internal.h"
#include "tensorflow/lite/core/api/error_reporter.h"
#include "tensorflow/lite/core/api/op_resolver.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/create_op_resolver.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/core/interpreter_builder.h"
#include "tensorflow/lite/core/model_builder.h"
#include "tensorflow/lite/core/signature_runner.h"
#include "tensorflow/lite/delegates/interpreter_utils.h"
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/mutable_op_resolver.h"
#include "tensorflow/lite/profiling/telemetry/profiler.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/stderr_reporter.h"
#include "tensorflow/lite/version.h"
namespace {
class CallbackErrorReporter : public tflite::ErrorReporter {
public:
explicit CallbackErrorReporter(TfLiteErrorReporterCallback callback)
: callback_(callback) {}
int Report(const char* format, va_list args) override {
callback_.error_reporter(callback_.user_data, format, args);
return 0;
}
private:
TfLiteErrorReporterCallback callback_;
};
} // namespace
extern "C" {
// LINT.IfChange
const char* TfLiteVersion() { return TFLITE_VERSION_STRING; }
int TfLiteSchemaVersion() { return TFLITE_SCHEMA_VERSION; }
const char* TfLiteExtensionApisVersion() {
return TFLITE_EXTENSION_APIS_VERSION_STRING;
}
TfLiteModel* TfLiteModelCreate(const void* model_data, size_t model_size) {
auto model = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(
static_cast<const char*>(model_data), model_size);
std::shared_ptr<const tflite::FlatBufferModel> shared_model(model.release());
return shared_model ? new TfLiteModel{std::move(shared_model)} : nullptr;
}
TfLiteModel* TfLiteModelCreateWithErrorReporter(
const void* model_data, size_t model_size,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data) {
struct TfLiteErrorReporterCallback er_cb = {user_data, reporter};
auto error_reporter = std::make_unique<CallbackErrorReporter>(er_cb);
auto model = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(
static_cast<const char*>(model_data), model_size, nullptr,
error_reporter.get());
std::shared_ptr<const tflite::FlatBufferModel> shared_model(model.release());
return shared_model ? new TfLiteModel{std::move(shared_model)} : nullptr;
}
TfLiteModel* TfLiteModelCreateFromFile(const char* model_path) {
auto model = tflite::FlatBufferModel::VerifyAndBuildFromFile(model_path);
std::shared_ptr<const tflite::FlatBufferModel> shared_model(model.release());
return shared_model ? new TfLiteModel{std::move(shared_model)} : nullptr;
}
TfLiteModel* TfLiteModelCreateFromFileWithErrorReporter(
const char* model_path,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data) {
struct TfLiteErrorReporterCallback er_cb = {user_data, reporter};
auto error_reporter = std::make_unique<CallbackErrorReporter>(er_cb);
auto model = tflite::FlatBufferModel::VerifyAndBuildFromFile(
model_path, nullptr, error_reporter.get());
std::shared_ptr<const tflite::FlatBufferModel> shared_model(model.release());
return shared_model ? new TfLiteModel{std::move(shared_model)} : nullptr;
}
void TfLiteModelDelete(TfLiteModel* model) { delete model; }
TfLiteInterpreterOptions* TfLiteInterpreterOptionsCreate() {
return new TfLiteInterpreterOptions{};
}
struct TfLiteInterpreterOptions* TfLiteInterpreterOptionsCopy(
const struct TfLiteInterpreterOptions* from) {
struct TfLiteInterpreterOptions* copy = new TfLiteInterpreterOptions{};
*copy = *from;
return copy;
}
void TfLiteInterpreterOptionsDelete(TfLiteInterpreterOptions* options) {
delete options;
}
void TfLiteInterpreterOptionsSetNumThreads(TfLiteInterpreterOptions* options,
int32_t num_threads) {
options->num_threads = num_threads;
}
void TfLiteInterpreterOptionsAddDelegate(TfLiteInterpreterOptions* options,
TfLiteOpaqueDelegate* delegate) {
options->delegates.push_back(delegate);
}
void TfLiteInterpreterOptionsSetErrorReporter(
TfLiteInterpreterOptions* options,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data) {
options->error_reporter_callback.error_reporter = reporter;
options->error_reporter_callback.user_data = user_data;
}
void TfLiteInterpreterOptionsAddOperator(TfLiteInterpreterOptions* options,
TfLiteOperator* registration) {
options->op_registrations.push_back(registration);
}
TfLiteStatus TfLiteInterpreterOptionsEnableCancellation(
TfLiteInterpreterOptions* options, bool enable) {
options->enable_cancellation = enable;
return kTfLiteOk;
}
static void InitTfLiteRegistration(TfLiteRegistration* registration,
TfLiteOperator* registration_external) {
registration->builtin_code = registration_external->builtin_code;
registration->custom_name = registration_external->custom_name;
registration->version = registration_external->version;
registration->registration_external = registration_external;
}
TfLiteInterpreter* TfLiteInterpreterCreate(
const TfLiteModel* model,
const TfLiteInterpreterOptions* optional_options) {
std::unique_ptr<tflite::MutableOpResolver> resolver =
tflite::CreateOpResolver();
return tflite::internal::InterpreterCreateWithOpResolver(
model, optional_options, resolver.get());
}
void TfLiteInterpreterDelete(TfLiteInterpreter* interpreter) {
delete interpreter;
}
int32_t TfLiteInterpreterGetInputTensorCount(
const TfLiteInterpreter* interpreter) {
return static_cast<int32_t>(interpreter->impl->inputs().size());
}
const int* TfLiteInterpreterInputTensorIndices(
const TfLiteInterpreter* interpreter) {
return interpreter->impl->inputs().data();
}
TfLiteTensor* TfLiteInterpreterGetInputTensor(
const TfLiteInterpreter* interpreter, int32_t input_index) {
return interpreter->impl->tensor(interpreter->impl->inputs()[input_index]);
}
TfLiteStatus TfLiteInterpreterResizeInputTensor(TfLiteInterpreter* interpreter,
int32_t input_index,
const int* input_dims,
int32_t input_dims_size) {
std::vector<int> dims{input_dims, input_dims + input_dims_size};
return interpreter->impl->ResizeInputTensor(
interpreter->impl->inputs()[input_index], dims);
}
TfLiteStatus TfLiteInterpreterAllocateTensors(TfLiteInterpreter* interpreter) {
return interpreter->impl->AllocateTensors();
}
TfLiteStatus TfLiteInterpreterInvoke(TfLiteInterpreter* interpreter) {
if (interpreter->enable_delegate_fallback) {
return tflite::delegates::InterpreterUtils::InvokeWithCPUFallback(
interpreter->impl.get());
} else {
return interpreter->impl->Invoke();
}
}
int32_t TfLiteInterpreterGetOutputTensorCount(
const TfLiteInterpreter* interpreter) {
return static_cast<int32_t>(interpreter->impl->outputs().size());
}
TfLiteTensor* TfLiteInterpreterGetTensor(const TfLiteInterpreter* interpreter,
int index) {
return interpreter->impl->tensor(index);
}
const int* TfLiteInterpreterOutputTensorIndices(
const TfLiteInterpreter* interpreter) {
return interpreter->impl->outputs().data();
}
const TfLiteTensor* TfLiteInterpreterGetOutputTensor(
const TfLiteInterpreter* interpreter, int32_t output_index) {
return interpreter->impl->tensor(interpreter->impl->outputs()[output_index]);
}
TfLiteStatus TfLiteInterpreterCancel(const TfLiteInterpreter* interpreter) {
return interpreter->impl->Cancel();
}
TfLiteType TfLiteTensorType(const TfLiteTensor* tensor) { return tensor->type; }
int32_t TfLiteTensorNumDims(const TfLiteTensor* tensor) {
if (!tensor->dims) {
return -1;
}
return tensor->dims->size;
}
int32_t TfLiteTensorDim(const TfLiteTensor* tensor, int32_t dim_index) {
return tensor->dims->data[dim_index];
}
size_t TfLiteTensorByteSize(const TfLiteTensor* tensor) {
return tensor->bytes;
}
void* TfLiteTensorData(const TfLiteTensor* tensor) { return tensor->data.raw; }
const char* TfLiteTensorName(const TfLiteTensor* tensor) {
return tensor->name;
}
TfLiteQuantizationParams TfLiteTensorQuantizationParams(
const TfLiteTensor* tensor) {
return tensor->params;
}
TfLiteStatus TfLiteTensorCopyFromBuffer(TfLiteTensor* tensor,
const void* input_data,
size_t input_data_size) {
if (tensor->bytes != input_data_size) {
return kTfLiteError;
}
memcpy(tensor->data.raw, input_data, input_data_size);
return kTfLiteOk;
}
TfLiteStatus TfLiteTensorCopyToBuffer(const TfLiteTensor* tensor,
void* output_data,
size_t output_data_size) {
if (tensor->bytes != output_data_size) {
return kTfLiteError;
}
memcpy(output_data, tensor->data.raw, output_data_size);
return kTfLiteOk;
}
// LINT.ThenChange(//tensorflow/lite/experimental/examples/unity/TensorFlowLitePlugin/Assets/TensorFlowLite/SDK/Scripts/Interpreter.cs)
int32_t TfLiteInterpreterGetSignatureCount(
const TfLiteInterpreter* interpreter) {
return static_cast<int32_t>(interpreter->impl->signature_keys().size());
}
const char* TfLiteInterpreterGetSignatureKey(
const TfLiteInterpreter* interpreter, int32_t signature_index) {
int32_t signature_count = TfLiteInterpreterGetSignatureCount(interpreter);
if (signature_index < 0 || signature_index >= signature_count) {
return nullptr;
}
return interpreter->impl->signature_keys()[signature_index]->c_str();
}
TfLiteSignatureRunner* TfLiteInterpreterGetSignatureRunner(
const TfLiteInterpreter* interpreter, const char* signature_key) {
tflite::SignatureRunner* signature_runner =
interpreter->impl->GetSignatureRunner(signature_key);
if (!signature_runner) return nullptr;
return new TfLiteSignatureRunner{signature_runner};
}
size_t TfLiteSignatureRunnerGetInputCount(
const TfLiteSignatureRunner* signature_runner) {
return signature_runner->impl->input_size();
}
const char* TfLiteSignatureRunnerGetInputName(
const TfLiteSignatureRunner* signature_runner, const int32_t input_index) {
int32_t input_count = TfLiteSignatureRunnerGetInputCount(signature_runner);
if (input_index < 0 || input_index >= input_count) {
return nullptr;
}
return signature_runner->impl->input_names()[input_index];
}
TfLiteStatus TfLiteSignatureRunnerResizeInputTensor(
TfLiteSignatureRunner* signature_runner, const char* input_name,
const int* input_dims, int32_t input_dims_size) {
std::vector<int> dims{input_dims, input_dims + input_dims_size};
return signature_runner->impl->ResizeInputTensorStrict(input_name, dims);
}
TfLiteStatus TfLiteSignatureRunnerAllocateTensors(
TfLiteSignatureRunner* signature_runner) {
return signature_runner->impl->AllocateTensors();
}
TfLiteTensor* TfLiteSignatureRunnerGetInputTensor(
TfLiteSignatureRunner* signature_runner, const char* input_name) {
return signature_runner->impl->input_tensor(input_name);
}
TfLiteStatus TfLiteSignatureRunnerInvoke(
TfLiteSignatureRunner* signature_runner) {
return signature_runner->impl->Invoke();
}
size_t TfLiteSignatureRunnerGetOutputCount(
const TfLiteSignatureRunner* signature_runner) {
return signature_runner->impl->output_size();
}
const char* TfLiteSignatureRunnerGetOutputName(
const TfLiteSignatureRunner* signature_runner, int32_t output_index) {
int32_t output_count = TfLiteSignatureRunnerGetOutputCount(signature_runner);
if (output_index < 0 || output_index >= output_count) {
return nullptr;
}
return signature_runner->impl->output_names()[output_index];
}
const TfLiteTensor* TfLiteSignatureRunnerGetOutputTensor(
const TfLiteSignatureRunner* signature_runner, const char* output_name) {
return signature_runner->impl->output_tensor(output_name);
}
TfLiteStatus TfLiteSignatureRunnerCancel(
TfLiteSignatureRunner* signature_runner) {
return signature_runner->impl->Cancel();
}
void TfLiteSignatureRunnerDelete(TfLiteSignatureRunner* signature_runner) {
delete signature_runner;
}
} // extern "C"
namespace tflite {
namespace internal {
static TfLiteRegistration* OperatorToRegistration(
const TfLiteOperator* registration_external) {
// All TfLiteOperator objects are dynamically allocated via
// TfLiteOperatorCreate(), so they are guaranteed
// to be mutable, hence the const_cast below should be safe.
auto registration_external_non_const =
const_cast<TfLiteOperator*>(registration_external);
TfLiteRegistration* new_registration = new TfLiteRegistration{};
InitTfLiteRegistration(new_registration, registration_external_non_const);
return new_registration;
}
// Implementation of CallbackOpResolver class which is defined in
// c_api_internal.h. CallbackOpResolver is a (C++) `tflite::OpResolver` that
// forwards the methods to (C ABI) callback functions from a
// `TfLiteOpResolverCallbacks` struct.
// FindOp for builtin op query.
const TfLiteRegistration* CallbackOpResolver::FindOp(tflite::BuiltinOperator op,
int version) const {
// Check if cached Registration is available.
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& created_registration : temporary_builtin_registrations_) {
if (created_registration->builtin_code == op &&
created_registration->version == version) {
return created_registration.get();
}
}
// Try using newer Operator API.
if (op_resolver_callbacks_.find_builtin_op_external) {
// Get a Operator object and create a Registration (V4) object.
const TfLiteOperator* registration_external =
op_resolver_callbacks_.find_builtin_op_external(
op_resolver_callbacks_.user_data,
static_cast<TfLiteBuiltinOperator>(op), version);
if (registration_external != nullptr &&
(registration_external->init != nullptr ||
registration_external->free != nullptr ||
registration_external->invoke != nullptr ||
registration_external->prepare != nullptr ||
registration_external->async_kernel != nullptr)) {
TfLiteRegistration* new_registration =
OperatorToRegistration(registration_external);
temporary_builtin_registrations_.push_back(
std::unique_ptr<TfLiteRegistration>(new_registration));
return new_registration;
}
}
// Use Registration V4 API to find op.
if (op_resolver_callbacks_.find_builtin_op) {
return op_resolver_callbacks_.find_builtin_op(
op_resolver_callbacks_.user_data,
static_cast<TfLiteBuiltinOperator>(op), version);
}
// Try using older Registration V3 API to find op.
if (auto* registration =
BuildBuiltinOpFromLegacyRegistration<TfLiteRegistration_V3>(
op, version, op_resolver_callbacks_.find_builtin_op_v3);
registration) {
return registration;
}
// Try using older Registration V2 API to find op.
if (auto* registration =
BuildBuiltinOpFromLegacyRegistration<TfLiteRegistration_V2>(
op, version, op_resolver_callbacks_.find_builtin_op_v2);
registration) {
return registration;
}
// Try using older Registration V1 API to find op.
if (auto* registration =
BuildBuiltinOpFromLegacyRegistration<TfLiteRegistration_V1>(
op, version, op_resolver_callbacks_.find_builtin_op_v1);
registration) {
return registration;
}
return nullptr;
}
// FindOp for custom op query.
const TfLiteRegistration* CallbackOpResolver::FindOp(const char* op,
int version) const {
// Check if cached Registration is available.
std::lock_guard<std::mutex> lock(mutex_);
for (const auto& created_registration : temporary_custom_registrations_) {
if (strcmp(created_registration->custom_name, op) == 0 &&
created_registration->version == version) {
return created_registration.get();
}
}
if (op_resolver_callbacks_.find_custom_op_external) {
// Get a Operator object and create a Registration (V3) object.
const TfLiteOperator* registration_external =
op_resolver_callbacks_.find_custom_op_external(
op_resolver_callbacks_.user_data, op, version);
if (registration_external && (registration_external->init != nullptr ||
registration_external->free != nullptr ||
registration_external->invoke != nullptr ||
registration_external->prepare != nullptr)) {
TfLiteRegistration* new_registration =
OperatorToRegistration(registration_external);
temporary_builtin_registrations_.push_back(
std::unique_ptr<TfLiteRegistration>(new_registration));
return new_registration;
}
}
// Use TfLiteRegistration V4 API to find op.
if (op_resolver_callbacks_.find_custom_op) {
return op_resolver_callbacks_.find_custom_op(
op_resolver_callbacks_.user_data, op, version);
}
// Use older TfLiteRegistration V3 API to find op.
if (auto* registration =
BuildCustomOpFromLegacyRegistration<TfLiteRegistration_V3>(
op, version, op_resolver_callbacks_.find_custom_op_v3);
registration) {
return registration;
}
// Use older TfLiteRegistration V2 API to find op.
if (auto* registration =
BuildCustomOpFromLegacyRegistration<TfLiteRegistration_V2>(
op, version, op_resolver_callbacks_.find_custom_op_v2);
registration) {
return registration;
}
// Use even older TfLiteRegistration V1 API to find op.
if (auto* registration =
BuildCustomOpFromLegacyRegistration<TfLiteRegistration_V1>(
op, version, op_resolver_callbacks_.find_custom_op_v1);
registration) {
return registration;
}
return nullptr;
}
TfLiteInterpreter* InterpreterCreateWithOpResolver(
const TfLiteModel* model, const TfLiteInterpreterOptions* optional_options,
tflite::MutableOpResolver* mutable_resolver) {
TFLITE_DCHECK_NE(mutable_resolver, nullptr);
if (!model || !model->impl) {
return nullptr;
}
std::unique_ptr<tflite::ErrorReporter> optional_error_reporter;
if (optional_options &&
optional_options->error_reporter_callback.error_reporter != nullptr) {
optional_error_reporter = std::make_unique<CallbackErrorReporter>(
optional_options->error_reporter_callback);
}
// By default, we use the provided mutable_op_resolver, adding any builtin or
// custom ops registered with `TfLiteInterpreterOptionsAddBuiltinOp` and/or
// `TfLiteInterpreterOptionsAddCustomOp`.
tflite::OpResolver* op_resolver = mutable_resolver;
if (optional_options) {
mutable_resolver->AddAll(optional_options->mutable_op_resolver);
for (auto* registration_external : optional_options->op_registrations) {
TfLiteRegistration registration{};
InitTfLiteRegistration(&registration, registration_external);
mutable_resolver->AddCustom(registration_external->custom_name,
&registration,
registration_external->version);
}
}
// However, if `TfLiteInterpreterOptionsSetOpResolver` has been called with
// a non-null callback parameter, then we instead use a
// `CallbackOpResolver` that will forward to the callbacks provided there.
CallbackOpResolver callback_op_resolver;
if (optional_options &&
(optional_options->op_resolver_callbacks.find_builtin_op != nullptr ||
optional_options->op_resolver_callbacks.find_custom_op != nullptr ||
optional_options->op_resolver_callbacks.find_builtin_op_v1 != nullptr ||
optional_options->op_resolver_callbacks.find_custom_op_v1 != nullptr ||
optional_options->op_resolver_callbacks.find_builtin_op_v2 != nullptr ||
optional_options->op_resolver_callbacks.find_custom_op_v2 != nullptr ||
optional_options->op_resolver_callbacks.find_builtin_op_v3 != nullptr ||
optional_options->op_resolver_callbacks.find_custom_op_v3 != nullptr ||
optional_options->op_resolver_callbacks.find_builtin_op_external !=
nullptr ||
optional_options->op_resolver_callbacks.find_custom_op_external !=
nullptr)) {
callback_op_resolver.SetCallbacks(optional_options->op_resolver_callbacks);
op_resolver = &callback_op_resolver;
}
tflite::ErrorReporter* error_reporter = optional_error_reporter
? optional_error_reporter.get()
: tflite::DefaultErrorReporter();
tflite::InterpreterBuilder builder(model->impl->GetModel(), *op_resolver,
error_reporter, nullptr,
model->impl->allocation());
if (optional_options && optional_options->telemetry_profiler) {
std::unique_ptr<tflite::telemetry::TelemetryProfiler> profiler;
profiler.reset(tflite::telemetry::MakeTfLiteTelemetryProfiler(
optional_options->telemetry_profiler));
builder.SetTelemetryProfiler(std::move(profiler));
}
std::unique_ptr<tflite::Interpreter> interpreter;
if (builder(&interpreter) != kTfLiteOk) {
return nullptr;
}
if (optional_options) {
if (optional_options->num_threads !=
TfLiteInterpreterOptions::kDefaultNumThreads) {
interpreter->SetNumThreads(optional_options->num_threads);
}
if (optional_options->use_nnapi) {
if (interpreter->ModifyGraphWithDelegate(tflite::NnApiDelegate()) !=
kTfLiteOk) {
return nullptr;
}
}
for (auto* delegate : optional_options->delegates) {
if (interpreter->ModifyGraphWithDelegate(delegate) != kTfLiteOk) {
return nullptr;
}
}
if (optional_options->enable_cancellation) {
interpreter->EnableCancellation();
}
}
bool enable_delegate_fallback =
optional_options != nullptr && optional_options->enable_delegate_fallback;
return new TfLiteInterpreter{model->impl, std::move(optional_error_reporter),
std::move(interpreter),
enable_delegate_fallback};
}
} // namespace internal
} // namespace tflite
+663
View File
@@ -0,0 +1,663 @@
/* 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.
==============================================================================*/
// WARNING: Users of TensorFlow Lite should not include this file directly, but
// should instead include "third_party/tensorflow/lite/c/c_api.h".
// Only the TensorFlow Lite implementation itself should include this file
// directly.
#ifndef TENSORFLOW_LITE_CORE_C_C_API_H_
#define TENSORFLOW_LITE_CORE_C_C_API_H_
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/core/async/c/types.h"
#include "tensorflow/lite/core/c/c_api_types.h" // IWYU pragma: export
#include "tensorflow/lite/core/c/operator.h" // IWYU pragma: export
/// C API for TensorFlow Lite.
///
/// The API leans towards simplicity and uniformity instead of convenience, as
/// most usage will be by language-specific wrappers. It provides largely the
/// same set of functionality as that of the C++ TensorFlow Lite `Interpreter`
/// API, but is useful for shared libraries where having a stable ABI boundary
/// is important.
///
/// Conventions:
/// * We use the prefix TfLite for everything in the API.
/// * size_t is used to represent byte sizes of objects that are
/// materialized in the address space of the calling process.
/// * int is used as an index into arrays.
///
/// Usage:
/// <pre><code>
/// // Create the model and interpreter options.
/// TfLiteModel* model = TfLiteModelCreateFromFile("/path/to/model.tflite");
/// TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();
/// TfLiteInterpreterOptionsSetNumThreads(options, 2);
///
/// // Create the interpreter.
/// TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
///
/// // Allocate tensors and populate the input tensor data.
/// TfLiteInterpreterAllocateTensors(interpreter);
/// TfLiteTensor* input_tensor =
/// TfLiteInterpreterGetInputTensor(interpreter, 0);
/// TfLiteTensorCopyFromBuffer(input_tensor, input.data(),
/// input.size() * sizeof(float));
///
/// // Execute inference.
/// TfLiteInterpreterInvoke(interpreter);
///
/// // Extract the output tensor data.
/// const TfLiteTensor* output_tensor =
/// TfLiteInterpreterGetOutputTensor(interpreter, 0);
/// TfLiteTensorCopyToBuffer(output_tensor, output.data(),
/// output.size() * sizeof(float));
///
/// // Dispose of the model and interpreter objects.
/// TfLiteInterpreterDelete(interpreter);
/// TfLiteInterpreterOptionsDelete(options);
/// TfLiteModelDelete(model);
/// </code></pre>
///
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/// \note Users of TensorFlow Lite should use
/// \code
/// #include "tensorflow/lite/c/c_api.h"
/// \endcode
/// to access the APIs documented on this page.
// NOLINTEND(whitespace/line_length)
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/** \defgroup c_api lite/c/c_api.h
* @{
*/
// NOLINTEND(whitespace/line_length)
// clang-format on
// This header should be valid in both C (e.g. C99) and C++,
// so 'void' in parameters is not redundant.
// NOLINTBEGIN(modernize-redundant-void-arg)
// --------------------------------------------------------------------------
// Opaque types used by the C API. (See also c_api_types.h.)
/// TfLiteModel wraps a loaded TensorFlow Lite model.
typedef struct TfLiteModel TfLiteModel;
/// TfLiteInterpreterOptions allows customized interpreter configuration.
typedef struct TfLiteInterpreterOptions TfLiteInterpreterOptions;
/// TfLiteInterpreter provides inference from a provided model.
typedef struct TfLiteInterpreter TfLiteInterpreter;
/// A tensor in the interpreter system which is a wrapper around a buffer of
/// data including a dimensionality (or NULL if not currently defined).
typedef struct TfLiteTensor TfLiteTensor;
/// TfLiteSignatureRunner is used to run inference on a signature.
///
/// Note: A signature is used to define a computation in a TF model. A model can
/// have multiple signatures. Each signature contains three components:
/// * Signature Key: A unique string to identify a signature
/// * Inputs: A list of names, each mapped to an input tensor of a signature
/// * Outputs: A list of names, each mapped to an output tensor of a signature
///
/// To learn more about signatures in TFLite, refer to:
/// https://www.tensorflow.org/lite/guide/signatures
///
/// Using the TfLiteSignatureRunner, for a particular signature, you can set its
/// inputs, invoke (i.e. execute) the computation, and retrieve its outputs.
typedef struct TfLiteSignatureRunner TfLiteSignatureRunner;
// --------------------------------------------------------------------------
/// The TensorFlow Lite Runtime version.
///
/// Returns a pointer to a statically allocated string that is the version
/// number of the (potentially dynamically loaded) TF Lite Runtime library.
/// TensorFlow Lite uses semantic versioning, and the return value should be
/// in semver 2 format <http://semver.org>, starting with MAJOR.MINOR.PATCH,
/// e.g. "2.12.0" or "2.13.0-rc2".
TFL_CAPI_EXPORT extern const char* TfLiteVersion(void);
// --------------------------------------------------------------------------
/// The TensorFlow Lite Extension APIs version.
///
/// Returns a pointer to a statically allocated string that is the version
/// number of the TF Lite Extension APIs supported by the (potentially
/// dynamically loaded) TF Lite Runtime library. The TF Lite "Extension APIs"
/// are the APIs for extending TF Lite with custom ops and delegates.
/// More specifically, this version number covers the (non-experimental)
/// functionality documented in the following header files:
///
/// * lite/c/c_api_opaque.h
/// * lite/c/common.h
/// * lite/c/builtin_op_data.h
/// * lite/builtin_ops.h
///
/// This version number uses semantic versioning, and the return value should
/// be in semver 2 format <http://semver.org>, starting with MAJOR.MINOR.PATCH,
/// e.g. "2.14.0" or "2.15.0-rc2".
TFL_CAPI_EXPORT extern const char* TfLiteExtensionApisVersion(void);
/// The supported TensorFlow Lite model file Schema version.
///
/// Returns the (major) version number of the Schema used for model
/// files that is supported by the (potentially dynamically loaded)
/// TensorFlow Lite Runtime.
///
/// Model files using schema versions different to this may not be supported by
/// the current version of the TF Lite Runtime.
TFL_CAPI_EXPORT int TfLiteSchemaVersion(void);
/// Returns a model from the provided buffer, or null on failure.
///
/// \note The caller retains ownership of the `model_data` buffer and should
/// ensure that the lifetime of the `model_data` buffer must be at least as long
/// as the lifetime of the `TfLiteModel` and of any `TfLiteInterpreter` objects
/// created from that `TfLiteModel`, and furthermore the contents of the
/// `model_data` buffer must not be modified during that time."
TFL_CAPI_EXPORT extern TfLiteModel* TfLiteModelCreate(const void* model_data,
size_t model_size);
/// Same as `TfLiteModelCreate` with customizble error reporter.
/// * `reporter` takes the provided `user_data` object, as well as a C-style
/// format string and arg list (see also vprintf).
/// * `user_data` is optional. If non-null, it is owned by the client and must
/// remain valid for the duration of the interpreter lifetime.
TFL_CAPI_EXPORT extern TfLiteModel* TfLiteModelCreateWithErrorReporter(
const void* model_data, size_t model_size,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data);
/// Returns a model from the provided file, or null on failure.
///
/// \note The file's contents must not be modified during the lifetime of the
/// `TfLiteModel` or of any `TfLiteInterpreter` objects created from that
/// `TfLiteModel`.
TFL_CAPI_EXPORT extern TfLiteModel* TfLiteModelCreateFromFile(
const char* model_path);
/// Same as `TfLiteModelCreateFromFile` with customizble error reporter.
/// * `reporter` takes the provided `user_data` object, as well as a C-style
/// format string and arg list (see also vprintf).
/// * `user_data` is optional. If non-null, it is owned by the client and must
/// remain valid for the duration of the interpreter lifetime.
TFL_CAPI_EXPORT extern TfLiteModel* TfLiteModelCreateFromFileWithErrorReporter(
const char* model_path,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data);
/// Destroys the model instance.
///
/// If `model` is a null pointer, this function has no effect.
TFL_CAPI_EXPORT extern void TfLiteModelDelete(TfLiteModel* model);
/// Returns a new interpreter options instances.
TFL_CAPI_EXPORT extern TfLiteInterpreterOptions*
TfLiteInterpreterOptionsCreate();
/// Creates and returns a shallow copy of an options object.
///
/// The caller is responsible for calling `TfLiteInterpreterOptionsDelete` to
/// deallocate the object pointed to by the returned pointer.
TFL_CAPI_EXPORT extern TfLiteInterpreterOptions* TfLiteInterpreterOptionsCopy(
const TfLiteInterpreterOptions* from);
/// Destroys the interpreter options instance.
///
/// If `options` is a null pointer, this function has no effect.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsDelete(
TfLiteInterpreterOptions* options);
/// Sets the number of CPU threads to use for the interpreter.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsSetNumThreads(
TfLiteInterpreterOptions* options, int32_t num_threads);
/// Adds a delegate to be applied during `TfLiteInterpreter` creation.
///
/// If delegate application fails, interpreter creation will also fail with an
/// associated error logged.
///
/// \note The caller retains ownership of the delegate and should ensure that it
/// remains valid for the duration of any created interpreter's lifetime.
///
/// If you are NOT using "TensorFlow Lite in Play Services", and NOT building
/// with `TFLITE_WITH_STABLE_ABI` or `TFLITE_USE_OPAQUE_DELEGATE` macros
/// enabled, it is possible to pass a `TfLiteDelegate*` rather than a
/// `TfLiteOpaqueDelegate*` to this function, since in those cases,
/// `TfLiteOpaqueDelegate` is just a typedef alias for `TfLiteDelegate`.
/// This is for compatibility with existing source code
/// and existing delegates. For new delegates, it is recommended to
/// use `TfLiteOpaqueDelegate` rather than `TfLiteDelegate`. (See
/// `TfLiteOpaqueDelegate` in tensorflow/lite/core/c/c_api_types.h.)
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsAddDelegate(
TfLiteInterpreterOptions* options, TfLiteOpaqueDelegate* delegate);
/// Sets a custom error reporter for interpreter execution.
///
/// * `reporter` takes the provided `user_data` object, as well as a C-style
/// format string and arg list (see also vprintf).
/// * `user_data` is optional. If non-null, it is owned by the client and must
/// remain valid for the duration of the interpreter lifetime.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsSetErrorReporter(
TfLiteInterpreterOptions* options,
void (*reporter)(void* user_data, const char* format, va_list args),
void* user_data);
/// Adds an op registration to be applied during `TfLiteInterpreter` creation.
///
/// The `TfLiteOperator` object is needed to implement custom op of
/// TFLite Interpreter via C API. Calling this function ensures that any
/// `TfLiteInterpreter` created with the specified `options` can execute models
/// that use the custom operator specified in `registration`.
/// Please refer https://www.tensorflow.org/lite/guide/ops_custom for custom op
/// support.
/// \note The caller retains ownership of the TfLiteOperator object
/// and should ensure that it remains valid for the duration of any created
/// interpreter's lifetime.
/// \warning This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsAddOperator(
TfLiteInterpreterOptions* options, TfLiteOperator* registration);
/// Enables users to cancel in-flight invocations with
/// `TfLiteInterpreterCancel`.
///
/// By default it is disabled and calling to `TfLiteInterpreterCancel` will
/// return kTfLiteError. See `TfLiteInterpreterCancel`.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterOptionsEnableCancellation(
TfLiteInterpreterOptions* options, bool enable);
/// Returns a new interpreter using the provided model and options, or null on
/// failure.
///
/// * `model` must be a valid model instance. The caller retains ownership of
/// the object, and may destroy it (via TfLiteModelDelete) immediately after
/// creating the interpreter. However, if the TfLiteModel was allocated with
/// TfLiteModelCreate, then the `model_data` buffer that was passed to
/// TfLiteModelCreate must outlive the lifetime of the TfLiteInterpreter
/// object that this function returns, and must not be modified during that
/// time; and if the TfLiteModel was allocated with TfLiteModelCreateFromFile,
/// then the contents of the model file must not be modified during the
/// lifetime of the TfLiteInterpreter object that this function returns.
/// * `optional_options` may be null. The caller retains ownership of the
/// object, and can safely destroy it (via TfLiteInterpreterOptionsDelete)
/// immediately after creating the interpreter.
///
/// \note The client *must* explicitly allocate tensors before attempting to
/// access input tensor data or invoke the interpreter.
TFL_CAPI_EXPORT extern TfLiteInterpreter* TfLiteInterpreterCreate(
const TfLiteModel* model, const TfLiteInterpreterOptions* optional_options);
/// Destroys the interpreter.
///
/// If `interpreter` is a null pointer, this function has no effect.
TFL_CAPI_EXPORT extern void TfLiteInterpreterDelete(
TfLiteInterpreter* interpreter);
/// Returns the number of input tensors associated with the model.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetInputTensorCount(
const TfLiteInterpreter* interpreter);
/// Returns a pointer to an array of input tensor indices. The length of the
/// array can be obtained via a call to `TfLiteInterpreterGetInputTensorCount`.
///
/// Typically the input tensors associated with an `interpreter` would be set
/// during the initialization of the `interpreter`, through a mechanism like the
/// `InterpreterBuilder`, and remain unchanged throughout the lifetime of the
/// interpreter. However, there are some circumstances in which the pointer may
/// not remain valid throughout the lifetime of the interpreter, because calls
/// to `SetInputs` on the interpreter invalidate the returned pointer.
///
/// The ownership of the array remains with the TFLite runtime.
TFL_CAPI_EXPORT const int* TfLiteInterpreterInputTensorIndices(
const TfLiteInterpreter* interpreter);
/// Returns the tensor associated with the input index.
/// REQUIRES: 0 <= input_index < TfLiteInterpreterGetInputTensorCount(tensor)
TFL_CAPI_EXPORT extern TfLiteTensor* TfLiteInterpreterGetInputTensor(
const TfLiteInterpreter* interpreter, int32_t input_index);
/// Resizes the specified input tensor.
///
/// \note After a resize, the client *must* explicitly allocate tensors before
/// attempting to access the resized tensor data or invoke the interpreter.
///
/// REQUIRES: 0 <= input_index < TfLiteInterpreterGetInputTensorCount(tensor)
///
/// This function makes a copy of the input dimensions, so the client can safely
/// deallocate `input_dims` immediately after this function returns.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterResizeInputTensor(
TfLiteInterpreter* interpreter, int32_t input_index, const int* input_dims,
int32_t input_dims_size);
/// Updates allocations for all tensors, resizing dependent tensors using the
/// specified input tensor dimensionality.
///
/// This is a relatively expensive operation, and need only be called after
/// creating the graph and/or resizing any inputs.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterAllocateTensors(
TfLiteInterpreter* interpreter);
/// Runs inference for the loaded graph.
///
/// Before calling this function, the caller should first invoke
/// TfLiteInterpreterAllocateTensors() and should also set the values for the
/// input tensors. After successfully calling this function, the values for the
/// output tensors will be set.
///
/// \note It is possible that the interpreter is not in a ready state to
/// evaluate (e.g., if AllocateTensors() hasn't been called, or if a
/// ResizeInputTensor() has been performed without a subsequent call to
/// AllocateTensors()).
///
/// If the (experimental!) delegate fallback option was enabled in the
/// interpreter options, then the interpreter will automatically fall back to
/// not using any delegates if execution with delegates fails. For details,
/// see TfLiteInterpreterOptionsSetEnableDelegateFallback in
/// c_api_experimental.h.
///
/// Returns one of the following status codes:
/// - kTfLiteOk: Success. Output is valid.
/// - kTfLiteDelegateError: Execution with delegates failed, due to a problem
/// with the delegate(s). If fallback was not enabled, output is invalid.
/// If fallback was enabled, this return value indicates that fallback
/// succeeded, the output is valid, and all delegates previously applied to
/// the interpreter have been undone.
/// - kTfLiteApplicationError: Same as for kTfLiteDelegateError, except that
/// the problem was not with the delegate itself, but rather was
/// due to an incompatibility between the delegate(s) and the
/// interpreter or model.
/// - kTfLiteError: Unexpected/runtime failure. Output is invalid.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterInvoke(
TfLiteInterpreter* interpreter);
/// Returns the number of output tensors associated with the model.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetOutputTensorCount(
const TfLiteInterpreter* interpreter);
/// Returns a pointer to an array of output tensor indices. The length of the
/// array can be obtained via a call to `TfLiteInterpreterGetOutputTensorCount`.
///
/// Typically the output tensors associated with an `interpreter` would be set
/// during the initialization of the `interpreter`, through a mechanism like the
/// `InterpreterBuilder`, and remain unchanged throughout the lifetime of the
/// interpreter. However, there are some circumstances in which the pointer may
/// not remain valid throughout the lifetime of the interpreter, because calls
/// to `SetOutputs` on the interpreter invalidate the returned pointer.
///
/// The ownership of the array remains with the TFLite runtime.
TFL_CAPI_EXPORT const int* TfLiteInterpreterOutputTensorIndices(
const TfLiteInterpreter* interpreter);
/// Returns the tensor associated with the output index.
/// REQUIRES: 0 <= output_index < TfLiteInterpreterGetOutputTensorCount(tensor)
///
/// \note The shape and underlying data buffer for output tensors may be not
/// be available until after the output tensor has been both sized and
/// allocated.
/// In general, best practice is to interact with the output tensor *after*
/// calling TfLiteInterpreterInvoke().
TFL_CAPI_EXPORT extern const TfLiteTensor* TfLiteInterpreterGetOutputTensor(
const TfLiteInterpreter* interpreter, int32_t output_index);
/// Returns modifiable access to the tensor that corresponds to the
/// specified `index` and is associated with the provided `interpreter`.
///
/// This requires the `index` to be between 0 and N - 1, where N is the
/// number of tensors in the model.
///
/// Typically the tensors associated with the `interpreter` would be set during
/// the `interpreter` initialization, through a mechanism like the
/// `InterpreterBuilder`, and remain unchanged throughout the lifetime of the
/// interpreter. However, there are some circumstances in which the pointer may
/// not remain valid throughout the lifetime of the interpreter, because calls
/// to `AddTensors` on the interpreter invalidate the returned pointer.
///
/// Note the difference between this function and
/// `TfLiteInterpreterGetInputTensor` (or `TfLiteInterpreterGetOutputTensor` for
/// that matter): `TfLiteInterpreterGetTensor` takes an index into the array of
/// all tensors associated with the `interpreter`'s model, whereas
/// `TfLiteInterpreterGetInputTensor` takes an index into the array of input
/// tensors.
///
/// The ownership of the tensor remains with the TFLite runtime, meaning the
/// caller should not deallocate the pointer.
TFL_CAPI_EXPORT
TfLiteTensor* TfLiteInterpreterGetTensor(const TfLiteInterpreter* interpreter,
int index);
/// Tries to cancel any in-flight invocation.
///
/// \note This only cancels `TfLiteInterpreterInvoke` calls that happen before
/// calling this and it does not cancel subsequent invocations.
/// \note Calling this function will also cancel any in-flight invocations of
/// SignatureRunners constructed from this interpreter.
/// Non-blocking and thread safe.
///
/// Returns kTfLiteError if cancellation is not enabled via
/// `TfLiteInterpreterOptionsEnableCancellation`.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterCancel(
const TfLiteInterpreter* interpreter);
/// --------------------------------------------------------------------------
/// SignatureRunner APIs
///
/// You can run inference by either:
///
/// (i) (recommended) using the Interpreter to initialize SignatureRunner(s) and
/// then only using SignatureRunner APIs.
///
/// (ii) only using Interpreter APIs.
///
/// NOTE:
/// * Only use one of the above options to run inference, i.e. avoid mixing both
/// SignatureRunner APIs and Interpreter APIs to run inference as they share
/// the same underlying data (e.g. updating an input tensor “A” retrieved
/// using the Interpreter APIs will update the state of the input tensor “B”
/// retrieved using SignatureRunner APIs, if they point to the same underlying
/// tensor in the model; as it is not possible for a user to debug this by
/// analyzing the code, it can lead to undesirable behavior).
/// * The TfLiteSignatureRunner type is conditionally thread-safe, provided that
/// no two threads attempt to simultaneously access two TfLiteSignatureRunner
/// instances that point to the same underlying signature, or access a
/// TfLiteSignatureRunner and its underlying TfLiteInterpreter, unless all
/// such simultaneous accesses are reads (rather than writes).
/// * The lifetime of a TfLiteSignatureRunner object ends when
/// TfLiteSignatureRunnerDelete() is called on it (or when the lifetime of the
/// underlying TfLiteInterpreter ends -- but you should call
/// TfLiteSignatureRunnerDelete() before that happens in order to avoid
/// resource leaks).
/// * You can only apply delegates to the interpreter (via
/// TfLiteInterpreterOptions) and not to a signature.
/// Returns the number of signatures defined in the model.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetSignatureCount(
const TfLiteInterpreter* interpreter);
/// Returns the key of the Nth signature in the model, where N is specified as
/// `signature_index`.
///
/// NOTE: The lifetime of the returned key is the same as (and depends on) the
/// lifetime of `interpreter`.
TFL_CAPI_EXPORT extern const char* TfLiteInterpreterGetSignatureKey(
const TfLiteInterpreter* interpreter, int32_t signature_index);
/// Returns a new signature runner using the provided interpreter and signature
/// key, or nullptr on failure.
///
/// NOTE: `signature_key` is a null-terminated C string that must match the
/// key of a signature in the interpreter's model.
///
/// NOTE: The returned signature runner should be destroyed, by calling
/// TfLiteSignatureRunnerDelete(), before the interpreter is destroyed.
TFL_CAPI_EXPORT extern TfLiteSignatureRunner*
TfLiteInterpreterGetSignatureRunner(const TfLiteInterpreter* interpreter,
const char* signature_key);
/// Returns the number of inputs associated with a signature.
TFL_CAPI_EXPORT extern size_t TfLiteSignatureRunnerGetInputCount(
const TfLiteSignatureRunner* signature_runner);
/// Returns the (null-terminated) name of the Nth input in a signature, where N
/// is specified as `input_index`.
///
/// NOTE: The lifetime of the returned name is the same as (and depends on) the
/// lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern const char* TfLiteSignatureRunnerGetInputName(
const TfLiteSignatureRunner* signature_runner, int32_t input_index);
/// Resizes the input tensor identified as `input_name` to be the dimensions
/// specified by `input_dims` and `input_dims_size`. Only unknown dimensions can
/// be resized with this function. Unknown dimensions are indicated as `-1` in
/// the `dims_signature` attribute of a TfLiteTensor.
///
/// Returns status of failure or success. Note that this doesn't actually resize
/// any existing buffers. A call to TfLiteSignatureRunnerAllocateTensors() is
/// required to change the tensor input buffer.
///
/// NOTE: This function is similar to TfLiteInterpreterResizeInputTensorStrict()
/// and not TfLiteInterpreterResizeInputTensor().
///
/// NOTE: `input_name` must match the name of an input in the signature.
///
/// NOTE: This function makes a copy of the input dimensions, so the caller can
/// safely deallocate `input_dims` immediately after this function returns.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteSignatureRunnerResizeInputTensor(
TfLiteSignatureRunner* signature_runner, const char* input_name,
const int* input_dims, int32_t input_dims_size);
/// Updates allocations for tensors associated with a signature and resizes
/// dependent tensors using the specified input tensor dimensionality.
/// This is a relatively expensive operation and hence should only be called
/// after initializing the signature runner object and/or resizing any inputs.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteSignatureRunnerAllocateTensors(
TfLiteSignatureRunner* signature_runner);
/// Returns the input tensor identified by `input_name` in the given signature.
/// Returns nullptr if the given name is not valid.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern TfLiteTensor* TfLiteSignatureRunnerGetInputTensor(
TfLiteSignatureRunner* signature_runner, const char* input_name);
/// Runs inference on a given signature.
///
/// Before calling this function, the caller should first invoke
/// TfLiteSignatureRunnerAllocateTensors() and should also set the values for
/// the input tensors. After successfully calling this function, the values for
/// the output tensors will be set.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteSignatureRunnerInvoke(
TfLiteSignatureRunner* signature_runner);
/// Returns the number of output tensors associated with the signature.
TFL_CAPI_EXPORT extern size_t TfLiteSignatureRunnerGetOutputCount(
const TfLiteSignatureRunner* signature_runner);
/// Returns the (null-terminated) name of the Nth output in a signature, where
/// N is specified as `output_index`.
///
/// NOTE: The lifetime of the returned name is the same as (and depends on) the
/// lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern const char* TfLiteSignatureRunnerGetOutputName(
const TfLiteSignatureRunner* signature_runner, int32_t output_index);
/// Returns the output tensor identified by `output_name` in the given
/// signature. Returns nullptr if the given name is not valid.
///
/// NOTE: The lifetime of the returned tensor is the same as (and depends on)
/// the lifetime of `signature_runner`.
TFL_CAPI_EXPORT extern const TfLiteTensor* TfLiteSignatureRunnerGetOutputTensor(
const TfLiteSignatureRunner* signature_runner, const char* output_name);
// --------------------------------------------------------------------------
// TfLiteTensor wraps data associated with a graph tensor.
//
// Note that, while the TfLiteTensor struct is not currently opaque, and its
// fields can be accessed directly, these methods are still convenient for
// language bindings. In the future the tensor struct will likely be made opaque
// in the public API.
/// Returns the type of a tensor element.
TFL_CAPI_EXPORT extern TfLiteType TfLiteTensorType(const TfLiteTensor* tensor);
/// Returns the number of dimensions that the tensor has. Returns -1 in case
/// the 'opaque_tensor' does not have its dimensions property set.
TFL_CAPI_EXPORT extern int32_t TfLiteTensorNumDims(const TfLiteTensor* tensor);
/// Returns the length of the tensor in the "dim_index" dimension.
/// REQUIRES: 0 <= dim_index < TFLiteTensorNumDims(tensor)
TFL_CAPI_EXPORT extern int32_t TfLiteTensorDim(const TfLiteTensor* tensor,
int32_t dim_index);
/// Returns the size of the underlying data in bytes.
TFL_CAPI_EXPORT extern size_t TfLiteTensorByteSize(const TfLiteTensor* tensor);
/// Returns a pointer to the underlying data buffer.
///
/// \note The result may be null if tensors have not yet been allocated, e.g.,
/// if the Tensor has just been created or resized and `TfLiteAllocateTensors()`
/// has yet to be called, or if the output tensor is dynamically sized and the
/// interpreter hasn't been invoked.
TFL_CAPI_EXPORT extern void* TfLiteTensorData(const TfLiteTensor* tensor);
/// Returns the (null-terminated) name of the tensor.
TFL_CAPI_EXPORT extern const char* TfLiteTensorName(const TfLiteTensor* tensor);
/// Returns the parameters for asymmetric quantization. The quantization
/// parameters are only valid when the tensor type is `kTfLiteUInt8` and the
/// `scale != 0`. Quantized values can be converted back to float using:
/// real_value = scale * (quantized_value - zero_point);
TFL_CAPI_EXPORT extern TfLiteQuantizationParams TfLiteTensorQuantizationParams(
const TfLiteTensor* tensor);
/// Copies from the provided input buffer into the tensor's buffer.
/// REQUIRES: input_data_size == TfLiteTensorByteSize(tensor)
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteTensorCopyFromBuffer(
TfLiteTensor* tensor, const void* input_data, size_t input_data_size);
/// Copies to the provided output buffer from the tensor's buffer.
/// REQUIRES: output_data_size == TfLiteTensorByteSize(tensor)
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteTensorCopyToBuffer(
const TfLiteTensor* output_tensor, void* output_data,
size_t output_data_size);
/// Destroys the signature runner.
///
/// If `signature_runner` is a null pointer, this function has no effect.
TFL_CAPI_EXPORT extern void TfLiteSignatureRunnerDelete(
TfLiteSignatureRunner* signature_runner);
// NOLINTEND(modernize-redundant-void-arg)
/** @} */
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_C_C_API_H_
@@ -0,0 +1,235 @@
/* 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/core/c/c_api_experimental.h"
#include <stdint.h>
#include <memory>
#include <vector>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/c_api_internal.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/interpreter.h"
#include "tensorflow/lite/profiling/telemetry/profiler.h"
#include "tensorflow/lite/signature_runner.h"
extern "C" {
TfLiteStatus TfLiteInterpreterResetVariableTensors(
TfLiteInterpreter* interpreter) {
return interpreter->impl->ResetVariableTensors();
}
int32_t TfLiteInterpreterGetVariableTensorCount(
const TfLiteInterpreter* interpreter) {
return static_cast<int32_t>(interpreter->impl->variables().size());
}
TfLiteTensor* TfLiteInterpreterGetVariableTensor(
const TfLiteInterpreter* interpreter, int32_t input_index) {
return interpreter->impl->tensor(interpreter->impl->variables()[input_index]);
}
void TfLiteInterpreterOptionsAddBuiltinOp(
TfLiteInterpreterOptions* options, TfLiteBuiltinOperator op,
const TfLiteRegistration* registration, int32_t min_version,
int32_t max_version) {
options->mutable_op_resolver.AddBuiltin(
static_cast<tflite::BuiltinOperator>(op), registration, min_version,
max_version);
}
TfLiteInterpreter* TfLiteInterpreterCreateWithSelectedOps(
const TfLiteModel* model,
const TfLiteInterpreterOptions* optional_options) {
tflite::MutableOpResolver resolver;
return tflite::internal::InterpreterCreateWithOpResolver(
model, optional_options, &resolver);
}
void TfLiteInterpreterOptionsAddCustomOp(TfLiteInterpreterOptions* options,
const char* name,
const TfLiteRegistration* registration,
int32_t min_version,
int32_t max_version) {
options->mutable_op_resolver.AddCustom(name, registration, min_version,
max_version);
}
void TfLiteInterpreterOptionsSetOpResolverExternal(
TfLiteInterpreterOptions* options,
const TfLiteOperator* (*find_builtin_op)(void* user_data, int op,
int version),
const TfLiteOperator* (*find_custom_op)(void* user_data,
const char* custom_op, int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op_external = find_builtin_op;
options->op_resolver_callbacks.find_custom_op_external = find_custom_op;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetOpResolverExternalWithFallback(
TfLiteInterpreterOptions* options,
const TfLiteOperator* (*find_builtin_op_external)(void* user_data, int op,
int version),
const TfLiteOperator* (*find_custom_op_external)(void* user_data,
const char* custom_op,
int version),
const TfLiteRegistration* (*find_builtin_op)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration* (*find_custom_op)(void* user_data, const char* op,
int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op_external =
find_builtin_op_external;
options->op_resolver_callbacks.find_custom_op_external =
find_custom_op_external;
options->op_resolver_callbacks.find_builtin_op = find_builtin_op;
options->op_resolver_callbacks.find_custom_op = find_custom_op;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetOpResolver(
TfLiteInterpreterOptions* options,
const TfLiteRegistration* (*find_builtin_op)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration* (*find_custom_op)(void* user_data, const char* op,
int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op = find_builtin_op;
options->op_resolver_callbacks.find_custom_op = find_custom_op;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetOpResolverV1(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V1* (*find_builtin_op_v1)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V1* (*find_custom_op_v1)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op_v1 = find_builtin_op_v1;
options->op_resolver_callbacks.find_custom_op_v1 = find_custom_op_v1;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetOpResolverV3(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V3* (*find_builtin_op_v3)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V3* (*find_custom_op_v3)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op_v3 = find_builtin_op_v3;
options->op_resolver_callbacks.find_custom_op_v3 = find_custom_op_v3;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetOpResolverV2(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V2* (*find_builtin_op_v2)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V2* (*find_custom_op_v2)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data) {
options->op_resolver_callbacks = {}; // Sets all fields to null.
options->op_resolver_callbacks.find_builtin_op_v2 = find_builtin_op_v2;
options->op_resolver_callbacks.find_custom_op_v2 = find_custom_op_v2;
options->op_resolver_callbacks.user_data = op_resolver_user_data;
}
void TfLiteInterpreterOptionsSetUseNNAPI(TfLiteInterpreterOptions* options,
bool enable) {
options->use_nnapi = enable;
}
void TfLiteInterpreterOptionsSetEnableDelegateFallback(
TfLiteInterpreterOptions* options, bool enable) {
options->enable_delegate_fallback = enable;
}
TfLiteStatus TfLiteInterpreterModifyGraphWithDelegate(
const TfLiteInterpreter* interpreter, TfLiteDelegate* delegate) {
return interpreter->impl->ModifyGraphWithDelegate(delegate);
}
int32_t TfLiteInterpreterGetInputTensorIndex(
const TfLiteInterpreter* interpreter, int32_t input_index) {
return interpreter->impl->inputs()[input_index];
}
int32_t TfLiteInterpreterGetOutputTensorIndex(
const TfLiteInterpreter* interpreter, int32_t output_index) {
return interpreter->impl->outputs()[output_index];
}
TfLiteStatus TfLiteInterpreterSetBufferHandle(TfLiteInterpreter* interpreter,
TfLiteTensor* tensor,
TfLiteBufferHandle buffer_handle,
TfLiteOpaqueDelegate* delegate) {
return interpreter->impl->SetBufferHandle(tensor, buffer_handle, delegate);
}
TfLiteStatus TfLiteInterpreterGetBufferHandle(TfLiteInterpreter* interpreter,
int tensor_index,
TfLiteBufferHandle* buffer_handle,
TfLiteOpaqueDelegate** delegate) {
return interpreter->impl->GetBufferHandle(tensor_index, buffer_handle,
delegate);
}
void TfLiteSetAllowBufferHandleOutput(const TfLiteInterpreter* interpreter,
bool allow_buffer_handle_output) {
interpreter->impl->SetAllowBufferHandleOutput(allow_buffer_handle_output);
}
TfLiteStatus TfLiteInterpreterSetCustomAllocationForTensor(
TfLiteInterpreter* interpreter, int tensor_index,
const TfLiteCustomAllocation* allocation, int64_t flags) {
if (allocation == nullptr) {
return kTfLiteError;
}
return interpreter->impl->SetCustomAllocationForTensor(tensor_index,
*allocation, flags);
}
TfLiteStatus TfLiteInterpreterEnsureTensorDataIsReadable(
TfLiteInterpreter* interpreter, int tensor_index) {
return interpreter->impl->EnsureTensorDataIsReadable(tensor_index);
}
void TfLiteInterpreterOptionsSetTelemetryProfiler(
TfLiteInterpreterOptions* options,
TfLiteTelemetryProfilerStruct* profiler) {
options->telemetry_profiler = profiler;
}
} // extern "C"
+414
View File
@@ -0,0 +1,414 @@
/* 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.
==============================================================================*/
/// WARNING: Users of TensorFlow Lite should not include this file directly,
/// but should instead include
/// "third_party/tensorflow/lite/c/c_api_experimental.h".
/// Only the TensorFlow Lite implementation itself should include this
/// file directly.
#ifndef TENSORFLOW_LITE_CORE_C_C_API_EXPERIMENTAL_H_
#define TENSORFLOW_LITE_CORE_C_C_API_EXPERIMENTAL_H_
#include <stdint.h>
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/c/c_api_types.h"
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/c/common.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// Resets all variable tensors to zero.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterResetVariableTensors(
TfLiteInterpreter* interpreter);
// Returns the number of variable tensors associated with the model.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetVariableTensorCount(
const TfLiteInterpreter* interpreter);
// Returns the tensor associated with the variable tensor index.
// REQUIRES: 0 <= input_index <
// TfLiteInterpreterGetVariableTensorCount(interpreter)
TFL_CAPI_EXPORT extern TfLiteTensor* TfLiteInterpreterGetVariableTensor(
const TfLiteInterpreter* interpreter, int32_t variable_index);
/// Adds an op registration for a builtin operator.
///
/// Op registrations are used to map ops referenced in the flatbuffer model
/// to executable function pointers (`TfLiteRegistration`s).
///
/// NOTE: The interpreter will make a shallow copy of `registration` internally,
/// so the caller should ensure that its contents (function pointers, etc...)
/// remain valid for the duration of the interpreter's lifetime. A common
/// practice is making the provided `TfLiteRegistration` instance static.
///
/// Code that uses this function should NOT call
/// `TfLiteInterpreterOptionsSetOpResolver` (or related functions) on the same
/// options object.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT void TfLiteInterpreterOptionsAddBuiltinOp(
TfLiteInterpreterOptions* options, TfLiteBuiltinOperator op,
const TfLiteRegistration* registration, int32_t min_version,
int32_t max_version);
/// Adds an op registration for a custom operator.
///
/// Op registrations are used to map ops referenced in the flatbuffer model
/// to executable function pointers (`TfLiteRegistration`s).
///
/// NOTE: The interpreter will make a shallow copy of `registration` internally,
/// so the caller should ensure that its contents (function pointers, etc...)
/// remain valid for the duration of any created interpreter's lifetime. A
/// common practice is making the provided `TfLiteRegistration` instance static.
///
/// The lifetime of the string pointed to by `name` must be at least as long
/// as the lifetime of the `TfLiteInterpreterOptions`.
///
/// Code that uses this function should NOT call
/// `TfLiteInterpreterOptionsSetOpResolver` (or related functions) on the same
/// options object.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT void TfLiteInterpreterOptionsAddCustomOp(
TfLiteInterpreterOptions* options, const char* name,
const TfLiteRegistration* registration, int32_t min_version,
int32_t max_version);
/// Registers callbacks for resolving builtin or custom operators.
///
/// The `TfLiteInterpreterOptionsSetOpResolverExternal` function provides an
/// alternative method for registering builtin ops and/or custom ops, by
/// providing operator resolver callbacks. Unlike using
/// `TfLiteInterpreterOptionsAddOperator`,
/// `TfLiteInterpreterOptionsAddBuiltinOp` and/or
/// `TfLiteInterpreterOptionsAddAddCustomOp`, these let you register all the
/// operators in a single call.
///
/// Code that uses this function should NOT call
/// `TfLiteInterpreterOptionsAddBuiltin` or
/// `TfLiteInterpreterOptionsAddCustomOp` on the same options object.
///
/// If `op_resolver_user_data` is non-null, its lifetime must be at least as
/// long as the lifetime of the `TfLiteInterpreterOptions`.
///
/// The TfLiteOperator objects whose addresses are returned by
/// `find_builtin_op` and `find_custom_op` must outlive both the
/// InterpreterOptions object and any Interpreter object created from it.
///
/// WARNING: This is an experimental API and subject to change.
void TfLiteInterpreterOptionsSetOpResolverExternal(
TfLiteInterpreterOptions* options,
const TfLiteOperator* (*find_builtin_op)(void* user_data, int op,
int version),
const TfLiteOperator* (*find_custom_op)(void* user_data,
const char* custom_op, int version),
void* op_resolver_user_data);
/// \private
/// Registers callbacks for resolving builtin or custom operators.
///
/// This combines the effects of TfLiteInterpreterOptionsSetOpResolverExternal
/// and TfLiteInterpreterOptionsSetOpResolver. The callbacks that return
/// TfLiteOperator will be called first, but if they return a
/// TfLiteOperator object that has no methods set, then
/// the callbacks that return a TfLiteRegistration will be called to get
/// the methods.
///
/// WARNING: This function is experimental and subject to change.
///
/// WARNING: This function is not an official part of the API,
/// and should not be used by apps. It is intended for use only from
/// TF Lite itself.
void TfLiteInterpreterOptionsSetOpResolverExternalWithFallback(
TfLiteInterpreterOptions* options,
const TfLiteOperator* (*find_builtin_op_external)(void* user_data, int op,
int version),
const TfLiteOperator* (*find_custom_op_external)(void* user_data,
const char* custom_op,
int version),
const TfLiteRegistration* (*find_builtin_op)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration* (*find_custom_op)(void* user_data, const char* op,
int version),
void* op_resolver_user_data);
/// Registers callbacks for resolving builtin or custom operators.
///
/// The `TfLiteInterpreterOptionsSetOpResolver` function provides an alternative
/// method for registering builtin ops and/or custom ops, by providing operator
/// resolver callbacks. Unlike using `TfLiteInterpreterOptionsAddBuiltinOp`
/// and/or `TfLiteInterpreterOptionsAddAddCustomOp`, these let you register all
/// the operators in a single call.
///
/// Code that uses this function should NOT call
/// `TfLiteInterpreterOptionsAddBuiltin` or
/// `TfLiteInterpreterOptionsAddCustomOp` on the same options object.
///
/// If `op_resolver_user_data` is non-null, its lifetime must be at least as
/// long as the lifetime of the `TfLiteInterpreterOptions`.
///
/// WARNING: This is an experimental API and subject to change.
///
/// DEPRECATED: use TfLiteInterpreterOptionsSetOpResolverExternal instead.
void TfLiteInterpreterOptionsSetOpResolver(
TfLiteInterpreterOptions* options,
const TfLiteRegistration* (*find_builtin_op)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration* (*find_custom_op)(void* user_data,
const char* custom_op,
int version),
void* op_resolver_user_data);
/// \private
/// Backward-compat version of TfLiteInterpreterOptionsSetOpResolver.
///
/// WARNING: This function is deprecated / not an official part of the API, is
/// only for binary backwards compatibility, and should not be called.
void TfLiteInterpreterOptionsSetOpResolverV3(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V3* (*find_builtin_op_v3)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V3* (*find_custom_op_v3)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data);
/// \private
/// Backward-compat version of TfLiteInterpreterOptionsSetOpResolver.
///
/// WARNING: This function is deprecated / not an official part of the API, is
/// only for binary backwards compatibility, and should not be called.
void TfLiteInterpreterOptionsSetOpResolverV2(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V2* (*find_builtin_op_v2)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V2* (*find_custom_op_v2)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data);
/// \private
/// Backward-compat version of TfLiteInterpreterOptionsSetOpResolver.
///
/// WARNING: This function is deprecated / not an official part of the API, is
/// only for binary backwards compatibility, and should not be called.
void TfLiteInterpreterOptionsSetOpResolverV1(
TfLiteInterpreterOptions* options,
const TfLiteRegistration_V1* (*find_builtin_op_v1)(void* user_data,
TfLiteBuiltinOperator op,
int version),
const TfLiteRegistration_V1* (*find_custom_op_v1)(void* user_data,
const char* op,
int version),
void* op_resolver_user_data);
/// Returns a new interpreter using the provided model and options, or null on
/// failure, where the model uses only the operators explicitly added to the
/// options. This is the same as `TFLiteInterpreterCreate` from `c_api.h`,
/// except that the only operators that are supported are the ones registered
/// in `options` via calls to `TfLiteInterpreterOptionsSetOpResolver`,
/// `TfLiteInterpreterOptionsAddBuiltinOp`, and/or
/// `TfLiteInterpreterOptionsAddCustomOp`.
///
/// * `model` must be a valid model instance. The caller retains ownership of
/// the object, and can destroy it immediately after creating the interpreter;
/// the interpreter will maintain its own reference to the underlying model
/// data.
/// * `options` should not be null. The caller retains ownership of the object,
/// and can safely destroy it immediately after creating the interpreter.
///
/// NOTE: The client *must* explicitly allocate tensors before attempting to
/// access input tensor data or invoke the interpreter.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteInterpreter*
TfLiteInterpreterCreateWithSelectedOps(const TfLiteModel* model,
const TfLiteInterpreterOptions* options);
/// Enable or disable the NN API delegate for the interpreter (true to enable).
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsSetUseNNAPI(
TfLiteInterpreterOptions* options, bool enable);
/// Enable or disable CPU fallback for the interpreter (true to enable).
/// If enabled, TfLiteInterpreterInvoke will do automatic fallback from
/// executing with delegate(s) to regular execution without delegates
/// (i.e. on CPU).
///
/// Allowing the fallback is suitable only if both of the following hold:
/// - The caller is known not to cache pointers to tensor data across
/// TfLiteInterpreterInvoke calls.
/// - The model is not stateful (no variables, no LSTMs) or the state isn't
/// needed between batches.
///
/// When delegate fallback is enabled, TfLiteInterpreterInvoke will
/// behave as follows:
/// If one or more delegates were set in the interpreter options
/// (see TfLiteInterpreterOptionsAddDelegate),
/// AND inference fails,
/// then the interpreter will fall back to not using any delegates.
/// In that case, the previously applied delegate(s) will be automatically
/// undone, and an attempt will be made to return the interpreter to an
/// invokable state, which may invalidate previous tensor addresses,
/// and the inference will be attempted again, using input tensors with
/// the same value as previously set.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsSetEnableDelegateFallback(
TfLiteInterpreterOptions* options, bool enable);
/// Allow a delegate to look at the graph and modify the graph to handle
/// parts of the graph themselves. After this is called, the graph may
/// contain new nodes that replace 1 more nodes.
/// 'delegate' must outlive the interpreter.
/// Use `TfLiteInterpreterOptionsAddDelegate` instead of this unless
/// absolutely required.
/// Returns one of the following three status codes:
/// 1. kTfLiteOk: Success.
/// 2. kTfLiteDelegateError: Delegation failed due to an error in the
/// delegate. The Interpreter has been restored to its pre-delegation state.
/// NOTE: This undoes all delegates previously applied to the Interpreter.
/// 3. kTfLiteError: Unexpected/runtime failure.
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterModifyGraphWithDelegate(
const TfLiteInterpreter* interpreter, TfLiteDelegate* delegate);
/// Returns the tensor index corresponding to the input tensor
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetInputTensorIndex(
const TfLiteInterpreter* interpreter, int32_t input_index);
/// Returns the tensor index corresponding to the output tensor
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern int32_t TfLiteInterpreterGetOutputTensorIndex(
const TfLiteInterpreter* interpreter, int32_t output_index);
/// Assigns (or reassigns) a custom memory allocation for the given
/// tensor. `flags` is a bitmask, see TfLiteCustomAllocationFlags.
/// The runtime does NOT take ownership of the underlying memory.
///
/// NOTE: User needs to call TfLiteInterpreterAllocateTensors() after this.
/// Invalid/insufficient buffers will cause an error during
/// TfLiteInterpreterAllocateTensors or TfLiteInterpreterInvoke (in case of
/// dynamic shapes in the graph).
///
/// Parameters should satisfy the following conditions:
/// 1. tensor->allocation_type == kTfLiteArenaRw or kTfLiteArenaRwPersistent
/// In general, this is true for I/O tensors & variable tensors.
/// 2. allocation->data has the appropriate permissions for runtime access
/// (Read-only for inputs, Read-Write for others), and outlives
/// TfLiteInterpreter.
/// 3. allocation->bytes >= tensor->bytes.
/// This condition is checked again if any tensors are resized.
/// 4. allocation->data should be aligned to kDefaultTensorAlignment
/// defined in lite/util.h. (Currently 64 bytes)
/// This check is skipped if kTfLiteCustomAllocationFlagsSkipAlignCheck is
/// set through `flags`.
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus
TfLiteInterpreterSetCustomAllocationForTensor(
TfLiteInterpreter* interpreter, int tensor_index,
const TfLiteCustomAllocation* allocation, int64_t flags);
/// --------------------------------------------------------------------------
/// BufferHandle APIs
/// Sets the delegate buffer handle for the given tensor.
///
/// This function sets the buffer handle for a tensor that is used by other
/// computing hardware such as EdgeTpu. For example, EdgeTpu delegate imports a
/// tensor's memory into EdgeTpu's virtual address and returns a buffer handle.
/// Then EdgeTpu delegate calls this API to associate the tensor with the buffer
/// handle.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterSetBufferHandle(
TfLiteInterpreter* interpreter, TfLiteTensor* tensor,
TfLiteBufferHandle buffer_handle, TfLiteOpaqueDelegate* delegate);
/// Gets the delegate buffer handle, and the delegate which can process
/// the buffer handle.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterGetBufferHandle(
TfLiteInterpreter* interpreter, int tensor_index,
TfLiteBufferHandle* buffer_handle, TfLiteOpaqueDelegate** delegate);
/// Sets whether buffer handle output is allowed.
/// When using hardware delegation, Interpreter will make the data of output
/// tensors available in `tensor->data` by default. If the application can
/// consume the buffer handle directly (e.g. reading output from OpenGL
/// texture), it can set this flag to false, so Interpreter won't copy the
/// data from buffer handle to CPU memory.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern void TfLiteSetAllowBufferHandleOutput(
const TfLiteInterpreter* interpreter, bool allow_buffer_handle_output);
/// --------------------------------------------------------------------------
/// SignatureRunner APIs
/// Attempts to cancel in flight invocation if any.
/// This will not affect calls to `Invoke` that happen after this.
/// Non blocking and thread safe.
/// Returns kTfLiteError if cancellation is not enabled, otherwise returns
/// kTfLiteOk.
/// NOTE: Calling this function will cancel in-flight invocations
/// in all SignatureRunners built from the same interpreter.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteSignatureRunnerCancel(
TfLiteSignatureRunner* signature_runner);
// Forward declaration, to avoid need for dependency on
// tensorflow/lite/profiling/telemetry/profiler.h.
struct TfLiteTelemetryProfilerStruct;
/// Registers the telemetry profiler to the interpreter.
/// Note: The interpreter does not take the ownership of profiler, but callers
/// must ensure profiler->data outlives the lifespan of the interpreter.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern void TfLiteInterpreterOptionsSetTelemetryProfiler(
TfLiteInterpreterOptions* options,
struct TfLiteTelemetryProfilerStruct* profiler);
/// Ensures the data of the tensor at the given index is readable.
/// Note: If a delegate has been used, and `SetAllowBufferHandleOutput(true)`
/// has been called, tensor outputs may be stored as delegate buffer handles
/// whose data is not directly readable until this method has been called. In
/// such cases, this method will copy the data from the delegate buffer handle
/// to CPU memory.
///
/// WARNING: This is an experimental API and subject to change.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteInterpreterEnsureTensorDataIsReadable(
TfLiteInterpreter* interpreter, int tensor_index);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // TENSORFLOW_LITE_CORE_C_C_API_EXPERIMENTAL_H_
File diff suppressed because it is too large Load Diff
+711
View File
@@ -0,0 +1,711 @@
/* 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/core/c/c_api_opaque.h"
#include <stdarg.h>
#include <stdint.h>
#include <cstdio>
#include <vector>
#include "tensorflow/lite/c/c_api_opaque_internal.h"
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/c/c_api_types.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/subgraph.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/string_util.h"
#include "tensorflow/lite/util.h"
namespace {
const TfLiteTensor* Convert(const TfLiteOpaqueTensor* opaque_tensor) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueTensor and TfLiteTensor being equivalent.
return reinterpret_cast<const TfLiteTensor*>(opaque_tensor);
}
TfLiteTensor* Convert(TfLiteOpaqueTensor* opaque_tensor) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueTensor and TfLiteTensor being equivalent.
return reinterpret_cast<TfLiteTensor*>(opaque_tensor);
}
TfLiteNode* Convert(TfLiteOpaqueNode* opaque_node) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueNode and TfLiteNode being equivalent.
return reinterpret_cast<TfLiteNode*>(opaque_node);
}
const TfLiteNode* Convert(const TfLiteOpaqueNode* opaque_node) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueNode and TfLiteNode being equivalent.
return reinterpret_cast<const TfLiteNode*>(opaque_node);
}
const TfLiteContext* Convert(const TfLiteOpaqueContext* opaque_context) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent.
return reinterpret_cast<const TfLiteContext*>(opaque_context);
}
TfLiteContext* Convert(TfLiteOpaqueContext* opaque_context) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent.
return reinterpret_cast<TfLiteContext*>(opaque_context);
}
TfLiteOpaqueContext* Convert(TfLiteContext* tflite_context) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent.
return reinterpret_cast<TfLiteOpaqueContext*>(tflite_context);
}
const ::tflite::Subgraph* GetSubgraph(
const TfLiteOpaqueContext* opaque_context) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteContext::impl_ having type ::tflite::Subgraph*.
return reinterpret_cast<const ::tflite::Subgraph*>(
Convert(opaque_context)->impl_);
}
::tflite::Subgraph* GetSubgraph(TfLiteOpaqueContext* opaque_context) {
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteContext::impl_ having type ::tflite::Subgraph*.
return reinterpret_cast<::tflite::Subgraph*>(Convert(opaque_context)->impl_);
}
} // namespace
struct TfLiteOpaqueTensorBuilder {
TfLiteType type;
void* data;
TfLiteAllocationType allocation_type;
TfLiteQuantizationParams quantization_params;
TfLiteQuantization quantization;
};
TfLiteType TfLiteOpaqueTensorType(const TfLiteOpaqueTensor* opaque_tensor) {
return TfLiteTensorType(reinterpret_cast<const TfLiteTensor*>(opaque_tensor));
}
int32_t TfLiteOpaqueTensorNumDims(const TfLiteOpaqueTensor* opaque_tensor) {
return TfLiteTensorNumDims(
reinterpret_cast<const TfLiteTensor*>(opaque_tensor));
}
int32_t TfLiteOpaqueTensorDim(const TfLiteOpaqueTensor* opaque_tensor,
int32_t dim_index) {
return TfLiteTensorDim(reinterpret_cast<const TfLiteTensor*>(opaque_tensor),
dim_index);
}
TfLiteStatus TfLiteOpaqueTensorGetNumDimsSignature(
const TfLiteOpaqueTensor* opaque_tensor, int32_t* num_dims) {
const TfLiteTensor* tensor = Convert(opaque_tensor);
if (!tensor->dims_signature) {
*num_dims = -1;
return kTfLiteOk;
}
*num_dims = tensor->dims_signature->size;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueTensorGetDimSignature(
const TfLiteOpaqueTensor* opaque_tensor, int32_t dim_index,
int32_t* dim_length) {
const TfLiteTensor* tensor = Convert(opaque_tensor);
const TfLiteIntArray* dims_signature = TfLiteTensorGetDimsSignature(tensor);
*dim_length = dims_signature->data[dim_index];
return kTfLiteOk;
}
int TfLiteOpaqueTensorIsVariable(const TfLiteOpaqueTensor* opaque_tensor) {
return Convert(opaque_tensor)->is_variable ? 1 : 0;
}
size_t TfLiteOpaqueTensorByteSize(const TfLiteOpaqueTensor* opaque_tensor) {
return TfLiteTensorByteSize(
reinterpret_cast<const TfLiteTensor*>(opaque_tensor));
}
void* TfLiteOpaqueTensorData(const TfLiteOpaqueTensor* opaque_tensor) {
return opaque_tensor != nullptr
? TfLiteTensorData(
reinterpret_cast<const TfLiteTensor*>(opaque_tensor))
: nullptr;
}
TfLiteAllocationType TfLiteOpaqueTensorGetAllocationType(
const TfLiteOpaqueTensor* opaque_tensor) {
return Convert(opaque_tensor)->allocation_type;
}
TfLiteAllocationStrategy TfLiteOpaqueTensorGetAllocationStrategy(
const TfLiteOpaqueTensor* t) {
return TfLiteTensorGetAllocationStrategy(Convert(t));
}
TfLiteRunStability TfLiteOpaqueTensorGetBufferAddressStability(
const TfLiteOpaqueTensor* t) {
return TfLiteTensorGetBufferAddressStability(Convert(t));
}
TfLiteRunStability TfLiteOpaqueTensorGetDataStability(
const TfLiteOpaqueTensor* t) {
return TfLiteTensorGetDataStability(Convert(t));
}
TfLiteRunStep TfLiteOpaqueTensorGetDataKnownStep(const TfLiteOpaqueTensor* t) {
return TfLiteTensorGetDataKnownStep(Convert(t));
}
TfLiteRunStep TfLiteOpaqueTensorGetShapeKnownStep(const TfLiteOpaqueTensor* t) {
return TfLiteTensorGetShapeKnownStep(Convert(t));
}
const char* TfLiteOpaqueTensorName(const TfLiteOpaqueTensor* opaque_tensor) {
return TfLiteTensorName(reinterpret_cast<const TfLiteTensor*>(opaque_tensor));
}
TfLiteQuantization TfLiteOpaqueTensorGetQuantization(
const TfLiteOpaqueTensor* opaque_tensor) {
return Convert(opaque_tensor)->quantization;
}
TfLiteQuantizationParams TfLiteOpaqueTensorGetQuantizationParams(
const TfLiteOpaqueTensor* opaque_tensor) {
return Convert(opaque_tensor)->params;
}
TfLiteStatus TfLiteOpaqueTensorCopyFromBuffer(TfLiteOpaqueTensor* opaque_tensor,
const void* input_data,
size_t input_data_size) {
return TfLiteTensorCopyFromBuffer(
reinterpret_cast<TfLiteTensor*>(opaque_tensor), input_data,
input_data_size);
}
TfLiteStatus TfLiteOpaqueTensorCopyToBuffer(
const TfLiteOpaqueTensor* opaque_tensor, void* output_data,
size_t output_data_size) {
return TfLiteTensorCopyToBuffer(
reinterpret_cast<const TfLiteTensor*>(opaque_tensor), output_data,
output_data_size);
}
int TfLiteOpaqueTensorGetStringCount(const TfLiteOpaqueTensor* tensor) {
return tflite::GetStringCount(Convert(tensor));
}
TfLiteStatus TfLiteOpaqueTensorGetString(const TfLiteOpaqueTensor* tensor,
int index, const char** str,
int* len) {
tflite::StringRef str_ref = tflite::GetString(Convert(tensor), index);
*str = str_ref.str;
*len = str_ref.len;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueTensorWriteStrings(TfLiteOpaqueTensor* tensor,
const char* const* str_array,
int str_array_len,
const int* str_n_len) {
tflite::DynamicBuffer buf;
for (int i = 0; i < str_array_len; ++i) {
buf.AddString(str_array[i], str_n_len[i]);
}
buf.WriteToTensorAsVector(Convert(tensor));
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueTensorWriteString(TfLiteOpaqueTensor* tensor,
const char* str, const int len) {
TfLiteOpaqueTensorWriteStrings(tensor, &str, 1, &len);
return kTfLiteOk;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderCreate() {
return new TfLiteOpaqueTensorBuilder{};
}
void TfLiteOpaqueTensorBuilderDelete(TfLiteOpaqueTensorBuilder* builder) {
delete builder;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetType(
TfLiteOpaqueTensorBuilder* builder, TfLiteType type) {
builder->type = type;
return builder;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetData(
TfLiteOpaqueTensorBuilder* builder, void* data) {
builder->data = data;
return builder;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetAllocationType(
TfLiteOpaqueTensorBuilder* builder, TfLiteAllocationType allocation_type) {
builder->allocation_type = allocation_type;
return builder;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetQuantizationParams(
TfLiteOpaqueTensorBuilder* builder, TfLiteQuantizationParams params) {
builder->quantization_params = params;
return builder;
}
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetQuantization(
TfLiteOpaqueTensorBuilder* builder, TfLiteQuantization quantization) {
builder->quantization = quantization;
return builder;
}
void TfLiteOpaqueTensorSetAllocationTypeToDynamic(TfLiteOpaqueTensor* tensor) {
tflite::SetTensorToDynamic(Convert(tensor));
}
void TfLiteOpaqueTensorSetNonCpuAllocation(TfLiteOpaqueTensor* opaque_tensor) {
Convert(opaque_tensor)->allocation_type = kTfLiteNonCpu;
}
const TfLiteOpaqueTensor* TfLiteOpaqueNodeGetInput(
const TfLiteOpaqueContext* opaque_context,
const TfLiteOpaqueNode* opaque_node, int index) {
const TfLiteTensor* tensor =
tflite::GetInput(reinterpret_cast<const TfLiteContext*>(opaque_context),
reinterpret_cast<const TfLiteNode*>(opaque_node), index);
return reinterpret_cast<const TfLiteOpaqueTensor*>(tensor);
}
TfLiteOpaqueTensor* TfLiteOpaqueNodeGetOutput(
TfLiteOpaqueContext* opaque_context, const TfLiteOpaqueNode* opaque_node,
int index) {
TfLiteTensor* tensor = tflite::GetOutput(
reinterpret_cast<TfLiteContext*>(opaque_context),
reinterpret_cast<const TfLiteNode*>(opaque_node), index);
return reinterpret_cast<TfLiteOpaqueTensor*>(tensor);
}
int TfLiteOpaqueNodeNumberOfInputs(const TfLiteOpaqueNode* opaque_node) {
return reinterpret_cast<const TfLiteNode*>(opaque_node)->inputs->size;
}
int TfLiteOpaqueNodeNumberOfOutputs(const TfLiteOpaqueNode* opaque_node) {
return reinterpret_cast<const TfLiteNode*>(opaque_node)->outputs->size;
}
void* TfLiteOpaqueNodeGetUserData(const TfLiteOpaqueNode* opaque_node) {
return reinterpret_cast<const TfLiteNode*>(opaque_node)->user_data;
}
void* TfLiteOpaqueNodeGetBuiltinData(const TfLiteOpaqueNode* opaque_node) {
return Convert(opaque_node)->builtin_data;
}
TfLiteStatus TfLiteOpaqueNodeGetCustomInitialData(
const TfLiteOpaqueNode* opaque_node, const void** init_data, int* size) {
*init_data = Convert(opaque_node)->custom_initial_data;
*size = Convert(opaque_node)->custom_initial_data_size;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueNodeInputs(const TfLiteOpaqueNode* opaque_node,
const int** inputs, int* num_inputs) {
const TfLiteNode* node = Convert(opaque_node);
*inputs = node->inputs->data;
*num_inputs = node->inputs->size;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueNodeOutputs(const TfLiteOpaqueNode* opaque_node,
const int** outputs, int* num_outputs) {
const TfLiteNode* node = Convert(opaque_node);
*outputs = node->outputs->data;
*num_outputs = node->outputs->size;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueNodeTemporaries(const TfLiteOpaqueNode* opaque_node,
const int** temporaries,
int* num_temporaries) {
const TfLiteNode* node = Convert(opaque_node);
*temporaries = node->temporaries->data;
*num_temporaries = node->temporaries->size;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueNodeSetTemporaries(TfLiteOpaqueNode* opaque_node,
const int* temporaries,
int num_temporaries) {
if (num_temporaries < 0) {
return kTfLiteError;
}
TfLiteNode* node = Convert(opaque_node);
TfLiteIntArrayFree(node->temporaries);
node->temporaries = TfLiteIntArrayCreate(num_temporaries);
for (int i = 0; i < num_temporaries; ++i) {
node->temporaries->data[i] = temporaries[i];
}
return kTfLiteOk;
}
int TfLiteOpaqueNodeGetInputTensorIndex(const TfLiteOpaqueNode* opaque_node,
int index_of_input) {
auto* node = Convert(opaque_node);
if (index_of_input < 0 || index_of_input >= node->inputs->size) {
return -1;
}
return node->inputs->data[index_of_input];
}
int TfLiteOpaqueNodeGetOutputTensorIndex(const TfLiteOpaqueNode* opaque_node,
int index_of_output) {
auto* node = Convert(opaque_node);
if (index_of_output < 0 || index_of_output >= node->outputs->size) {
return -1;
}
return node->outputs->data[index_of_output];
}
TfLiteStatus TfLiteOpaqueContextGetExecutionPlan(
TfLiteOpaqueContext* opaque_context, TfLiteIntArray** execution_plan) {
// The following casts are safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent.
auto context = reinterpret_cast<TfLiteContext*>(opaque_context);
return context->GetExecutionPlan(context, execution_plan);
}
TfLiteStatus TfLiteOpaqueContextGetExternalContext(
TfLiteOpaqueContext* opaque_context, void** external_context,
TfLiteExternalContextType type) {
auto context = reinterpret_cast<TfLiteContext*>(opaque_context);
*external_context = context->GetExternalContext(context, type);
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueContextGetNodeAndRegistration(
struct TfLiteOpaqueContext* opaque_context, int node_index,
TfLiteOpaqueNode** node, TfLiteOperator** registration_external) {
// The following casts are safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent, or on
// TfLiteOpaqueNode and TfLiteNode being equivalent.
TfLiteContext* context = reinterpret_cast<TfLiteContext*>(opaque_context);
TfLiteNode* local_node;
TfLiteRegistration* registration;
TfLiteStatus status = context->GetNodeAndRegistration(
context, node_index, &local_node, &registration);
if (status != kTfLiteOk) return status;
// When the `registration` object obtained via `GetNodeAndRegistration`
// has its `registration_external` field set then we can load that into the
// caller's `registration_external` pointer and return early.
*node = reinterpret_cast<TfLiteOpaqueNode*>(local_node);
if (registration->registration_external) {
*registration_external = registration->registration_external;
return kTfLiteOk;
}
// When the `registration` object obtained via `GetNodeAndRegistration`
// does *not* have its `registration_external` field set then we need to
// create a TfLiteOperator on the fly, and set its field according
// to the `TfLiteRegistration` object.
auto derived_registration =
tflite::internal::CommonOpaqueConversionUtil::ObtainOperator(
context, registration, node_index);
if (derived_registration == nullptr) return kTfLiteError;
*registration_external = derived_registration;
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueContextReplaceNodeSubsetsWithDelegateKernels(
struct TfLiteOpaqueContext* opaque_context,
TfLiteOperator* registration_external,
const TfLiteIntArray* nodes_to_replace,
TfLiteOpaqueDelegate* opaque_delegate) {
// The following casts are safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent, or on
// TfLiteOpaqueNode and TfLiteNode being equivalent.
TfLiteContext* context = reinterpret_cast<TfLiteContext*>(opaque_context);
TfLiteDelegate* delegate = reinterpret_cast<TfLiteDelegate*>(opaque_delegate);
// Wrap the provided `registration_external` as a regular `TfLiteRegistration`
// object to reduce the places in the TF Lite runtime that need to be aware
// of `TfLiteOperator`s. Note that it is important to
// brace-initialize the `TfLiteRegistration` so that we pass a registration to
// `ReplaceNodeSubsetsWithDelegateKernels` that has all of its fields set to
// null, except the `registration_external` one.
TfLiteRegistration registration{};
registration.registration_external = registration_external;
// Takes ownership of registration_external, if delegate is opaque delegate.
TfLiteStatus status = context->ReplaceNodeSubsetsWithDelegateKernels(
context, registration, nodes_to_replace, delegate);
return status;
}
TfLiteOpaqueTensor* TfLiteOpaqueContextGetOpaqueTensor(
const TfLiteOpaqueContext* opaque_context, int index) {
// The following casts are safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent, or on
// TfLiteTensor and TfLiteOpaqueTensor being equivalent.
auto context = reinterpret_cast<const TfLiteContext*>(opaque_context);
return reinterpret_cast<TfLiteOpaqueTensor*>(&context->tensors[index]);
}
TfLiteStatus TfLiteOpaqueContextGetInputs(
const struct TfLiteOpaqueContext* opaque_context, const int** inputs,
int* num_inputs) {
auto* subgraph = GetSubgraph(opaque_context);
const std::vector<int>& subgraph_inputs = subgraph->inputs();
*inputs = subgraph_inputs.data();
*num_inputs = subgraph_inputs.size();
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueContextGetOutputs(
const struct TfLiteOpaqueContext* opaque_context, const int** outputs,
int* num_outputs) {
auto* subgraph = GetSubgraph(opaque_context);
const std::vector<int>& subgraph_outputs = subgraph->outputs();
*outputs = subgraph_outputs.data();
*num_outputs = subgraph_outputs.size();
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueContextGetVariables(
const struct TfLiteOpaqueContext* opaque_context, const int** variables,
int* num_variables) {
auto* subgraph = GetSubgraph(opaque_context);
const std::vector<int>& subgraph_variables = subgraph->variables();
*variables = subgraph_variables.data();
*num_variables = subgraph_variables.size();
return kTfLiteOk;
}
size_t TfLiteOpaqueContextGetNumNodes(
const struct TfLiteOpaqueContext* opaque_context) {
auto* subgraph = GetSubgraph(opaque_context);
return subgraph->nodes_size();
}
size_t TfLiteOpaqueContextGetNumTensors(
const struct TfLiteOpaqueContext* opaque_context) {
auto* subgraph = GetSubgraph(opaque_context);
return subgraph->tensors_size();
}
const char* TfLiteOpaqueContextGetName(
const struct TfLiteOpaqueContext* opaque_context) {
auto* subgraph = GetSubgraph(opaque_context);
return subgraph->GetName().c_str();
}
TfLiteStatus TfLiteOpaqueContextResizeTensor(TfLiteOpaqueContext* context,
TfLiteOpaqueTensor* tensor,
TfLiteIntArray* new_size) {
// The following casts are safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent, or on
// TfLiteOpaqueTensor and TfLiteTensor being equivalent.
TfLiteContext* tflite_context = reinterpret_cast<TfLiteContext*>(context);
return tflite_context->ResizeTensor(
tflite_context, reinterpret_cast<TfLiteTensor*>(tensor), new_size);
}
TfLiteStatus TfLiteOpaqueContextAcquireSubgraphContext(
struct TfLiteOpaqueContext* opaque_context, int subgraph_index,
TfLiteOpaqueContext** acquired_opaque_context) {
auto* subgraph = GetSubgraph(opaque_context);
TfLiteContext* acquired_context;
TfLiteStatus status =
subgraph->AcquireSubgraphContext(subgraph_index, &acquired_context);
if (status != kTfLiteOk) {
return status;
}
*acquired_opaque_context = Convert(acquired_context);
return kTfLiteOk;
}
TfLiteStatus TfLiteOpaqueContextReleaseSubgraphContext(
struct TfLiteOpaqueContext* opaque_context, int subgraph_index) {
return GetSubgraph(opaque_context)->ReleaseSubgraphContext(subgraph_index);
}
TfLiteStatus TfLiteOpaqueContextMarkSubgraphAsDelegationSkippable(
TfLiteOpaqueContext* opaque_context, int subgraph_index) {
auto* subgraph = GetSubgraph(opaque_context);
return subgraph->MarkSubgraphAsDelegationSkippable(subgraph_index);
}
TfLiteStatus TfLiteOpaqueContextGetNodeInitDataMmapInfo(
const TfLiteOpaqueContext* context, const TfLiteOpaqueNode* node, int* fd,
int64_t* custom_initial_data_offset_in_file,
int64_t* custom_initial_data_size) {
auto* subgraph = GetSubgraph(context);
return subgraph->GetNodeInitDataMmapInfo(Convert(node), fd,
custom_initial_data_offset_in_file,
custom_initial_data_size);
}
TfLiteStatus TfLiteOpaqueContextAddTensor(TfLiteOpaqueContext* context,
TfLiteOpaqueTensorBuilder* builder,
int* new_tensor_index) {
if (builder->allocation_type != kTfLiteDynamic &&
builder->allocation_type != kTfLiteArenaRw &&
builder->allocation_type != kTfLiteArenaRwPersistent) {
TfLiteOpaqueContextReportError(
context,
"Invalid allocation type '%d'. Allocation type for "
"TfLiteOpaqueContextAddTensor must be one of: "
"'kTfLiteDynamic', 'kTfLiteArenaRw' or 'kTfLiteArenaRwPersistent'.",
builder->allocation_type);
return kTfLiteError;
}
if (builder->allocation_type == kTfLiteDynamic && builder->data == nullptr) {
TfLiteOpaqueContextReportError(context,
"For tensors of allocation type "
"'kTfLiteDynamic' 'data' must be provided.");
return kTfLiteError;
}
if ((builder->allocation_type == kTfLiteArenaRw ||
builder->allocation_type == kTfLiteArenaRwPersistent) &&
builder->data != nullptr) {
TfLiteOpaqueContextReportError(
context,
"For tensors of allocation type "
"'kTfLiteArenaRw' or 'kTfLiteArenaRwPersistent' "
"'data' must not be provided.");
return kTfLiteError;
}
auto* tflite_context = Convert(context);
int index = -1;
auto status = tflite_context->AddTensors(tflite_context, 1, &index);
if (status != kTfLiteOk) return status;
tflite_context->tensors[index].type = builder->type;
tflite_context->tensors[index].data.data = builder->data;
tflite_context->tensors[index].allocation_type = builder->allocation_type;
tflite_context->tensors[index].params = builder->quantization_params;
tflite_context->tensors[index].quantization = builder->quantization;
if (new_tensor_index != nullptr) {
*new_tensor_index = index;
}
return status;
}
TfLiteStatus TfLiteOpaqueContextGetSizeOfType(TfLiteOpaqueContext* context,
const TfLiteType type,
size_t* bytes) {
return tflite::GetSizeOfType(Convert(context), type, bytes);
}
TfLiteStatus TfLiteOpaqueContextGetMetadata(TfLiteOpaqueContext* context,
const char* name, const char** ptr,
size_t* bytes) {
auto* tflite_context = Convert(context);
return tflite_context->GetModelMetadata(tflite_context, name, ptr, bytes);
}
void TfLiteOpaqueContextReportError(struct TfLiteOpaqueContext* opaque_context,
const char* format, ...) {
va_list vlist;
va_start(vlist, format);
TfLiteOpaqueContextReportErrorVa(opaque_context, format, vlist);
va_end(vlist);
}
void TfLiteOpaqueContextReportErrorVa(
struct TfLiteOpaqueContext* opaque_context, const char* format,
va_list vlist) {
// Determine the length of the resulting error message.
va_list copy;
va_copy(copy, vlist);
int n = vsnprintf(nullptr, 0, format, copy);
if (n < 0) {
return;
}
size_t size = (size_t)n + 1; // +1 for '\0'.
char* buffer = new char[size];
n = vsnprintf(buffer, size, format, vlist);
if (n < 0) {
delete[] buffer;
return;
}
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// TfLiteOpaqueContext and TfLiteContext being equivalent.
auto* context = reinterpret_cast<TfLiteContext*>(opaque_context);
TF_LITE_KERNEL_LOG(context, "%s", buffer);
delete[] buffer;
}
#ifndef TF_LITE_STATIC_MEMORY
TfLiteOpaqueDelegate* 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<TfLiteOpaqueDelegate*>(result);
}
void TfLiteOpaqueDelegateDelete(TfLiteOpaqueDelegate* 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;
}
#endif // TF_LITE_STATIC_MEMORY
void* TfLiteOpaqueDelegateGetData(const TfLiteOpaqueDelegate* delegate) {
if (!delegate) return nullptr;
// The following cast is safe only because this code is part of the
// TF Lite runtime implementation. Apps using TF Lite should not rely on
// 'TfLiteOpaqueDelegate' and 'TfLiteDelegate' being equivalent.
const auto* tflite_delegate =
reinterpret_cast<const TfLiteDelegate*>(delegate);
if (!tflite_delegate->opaque_delegate_builder) return tflite_delegate->data_;
return tflite_delegate->opaque_delegate_builder->data;
}
+853
View File
@@ -0,0 +1,853 @@
/* 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.
==============================================================================*/
// WARNING: Users of TensorFlow Lite should not include this file directly, but
// should instead include "third_party/tensorflow/lite/c/c_api_opaque.h".
// Only the TensorFlow Lite implementation itself should include this file
// directly.
#ifndef TENSORFLOW_LITE_CORE_C_C_API_OPAQUE_H_
#define TENSORFLOW_LITE_CORE_C_C_API_OPAQUE_H_
#include <stddef.h>
#include <stdint.h>
#include "tensorflow/lite/core/c/c_api.h"
#include "tensorflow/lite/core/c/c_api_types.h" // IWYU pragma: export
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/core/c/operator.h" // IWYU pragma: export
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// --------------------------------------------------------------------------
/// C API for TensorFlow Lite Opaque Types.
///
/// These APIs are accessors for TFLite Opaque Types. These APIs are primarily
/// intended to be used by delegates and custom OP implementations.
///
/// This API is part of the TensorFlow Lite Extension APIs.
/// We reserve the right to make changes to this API in future releases,
/// potentially including non-backwards-compatible changes, on a different
/// schedule than for the other TensorFlow Lite APIs. See
/// https://www.tensorflow.org/guide/versions#separate_version_number_for_tensorflow_lite_extension_apis.
///
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/// \note Users of TensorFlow Lite should use
/// \code
/// #include "tensorflow/lite/c/c_api_opaque.h"
/// \endcode
/// to access the APIs documented on this page.
// NOLINTEND(whitespace/line_length)
// clang-format on
// clang-format off
// NOLINTBEGIN(whitespace/line_length)
/** \defgroup c_api_opaque lite/c/c_api_opaque.h
* @{
*/
// NOLINTEND(whitespace/line_length)
// clang-format on
// --------------------------------------------------------------------------
// Accessors for TfLiteOpaqueTensor.
/// Returns the type of a tensor element.
TFL_CAPI_EXPORT extern TfLiteType TfLiteOpaqueTensorType(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the number of dimensions that the tensor has. Returns -1 in case
/// the `opaque_tensor` does not have its dimensions property set.
TFL_CAPI_EXPORT extern int32_t TfLiteOpaqueTensorNumDims(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the length of the tensor in the "dim_index" dimension.
TFL_CAPI_EXPORT extern int32_t TfLiteOpaqueTensorDim(
const TfLiteOpaqueTensor* opaque_tensor, int32_t dim_index);
/// Loads into the provided `num_dims` the number of dimensions that the
/// tensor's signature has. Returns `kTfLiteOk` if `num_dims` was successfully
/// loaded. Any other return code indicates an error and `num_dims` won't be
/// loaded.
///
/// A tensor's dimension signature encodes shapes with unknown dimensions with
/// -1. E.g. for a tensor with three dimensions, whose first dimension has an
/// unknown size, and the second and third dimension have a size of 2, the
/// dimension signature is [-1,2,2], and `TfLiteOpaqueTensorGetNumDimsSignature`
/// loads 3 into `num_dims`. If the tensor does not have its dimension signature
/// field set then `num_dims` is set to -1.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueTensorGetNumDimsSignature(
const TfLiteOpaqueTensor* opaque_tensor, int32_t* num_dims);
/// Loads into the provided `dim_length` the length of the tensor in the
/// `dim_index` signature dimension or -1 if that dimension has unknown length.
/// Returns `kTfLiteOk` if `dim_length` was successfully loaded. Any
/// other return code indicates an error and `dim_length` won't be loaded.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueTensorGetDimSignature(
const TfLiteOpaqueTensor* opaque_tensor, int32_t dim_index,
int32_t* dim_length);
/// Returns `non-zero` if the provided `opaque_tensor` is a variable, and
/// returns zero otherwise.
TFL_CAPI_EXPORT extern int TfLiteOpaqueTensorIsVariable(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the size of the underlying data in bytes.
TFL_CAPI_EXPORT extern size_t TfLiteOpaqueTensorByteSize(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns a pointer to the underlying data buffer.
/// Returns nullptr if input is also nullptr.
TFL_CAPI_EXPORT extern void* TfLiteOpaqueTensorData(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the `opaque_tensor`'s allocation type.
TFL_CAPI_EXPORT extern TfLiteAllocationType TfLiteOpaqueTensorGetAllocationType(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns a tensor data allocation strategy.
TFL_CAPI_EXPORT extern TfLiteAllocationStrategy
TfLiteOpaqueTensorGetAllocationStrategy(const TfLiteOpaqueTensor* t);
/// Returns how stable a tensor data buffer address is across runs.
TFL_CAPI_EXPORT extern TfLiteRunStability
TfLiteOpaqueTensorGetBufferAddressStability(const TfLiteOpaqueTensor* t);
/// Returns how stable a tensor data values are across runs.
TFL_CAPI_EXPORT extern TfLiteRunStability TfLiteOpaqueTensorGetDataStability(
const TfLiteOpaqueTensor* t);
/// Returns the operation step when the data of a tensor is populated.
TFL_CAPI_EXPORT extern TfLiteRunStep TfLiteOpaqueTensorGetDataKnownStep(
const TfLiteOpaqueTensor* t);
/// Returns the operation step when the shape of a tensor is computed.
TFL_CAPI_EXPORT extern TfLiteRunStep TfLiteOpaqueTensorGetShapeKnownStep(
const TfLiteOpaqueTensor* t);
/// Returns the (null-terminated) name of the tensor.
TFL_CAPI_EXPORT extern const char* TfLiteOpaqueTensorName(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the `opaque_tensor`'s quantization information.
TFL_CAPI_EXPORT extern TfLiteQuantization TfLiteOpaqueTensorGetQuantization(
const TfLiteOpaqueTensor* opaque_tensor);
/// Returns the `opaque_tensor`'s quantization parameters.
TFL_CAPI_EXPORT extern TfLiteQuantizationParams
TfLiteOpaqueTensorGetQuantizationParams(
const TfLiteOpaqueTensor* opaque_tensor);
/// Copies from the provided input buffer into the tensor's buffer.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueTensorCopyFromBuffer(
TfLiteOpaqueTensor* opaque_tensor, const void* input_data,
size_t input_data_size);
/// Copies to the provided output buffer from the tensor's buffer.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueTensorCopyToBuffer(
const TfLiteOpaqueTensor* opaque_tensor, void* output_data,
size_t output_data_size);
/// Returns the number of strings stored in the provided `tensor`.
/// Returns -1 in case of failure.
int TfLiteOpaqueTensorGetStringCount(const TfLiteOpaqueTensor* tensor);
/// Stores the address of the n-th (denoted by the provided `index`) string
/// contained in the provided `tensor` in the provided `*str` pointer. Stores
/// the length of the string in the provided `*len` argument.
///
/// Returns `kTfLiteOk` if `*str` and `*len` have been set successfully. Any
/// other return value indicates a failure, which leaves `*str` and `*len` in an
/// unspecified state.
///
/// The range of valid indices is defined by the half open interval [0, N),
/// where N == TfLiteOpaqueTensorGetStringCount(tensor).
///
/// Note that `str` is not guaranteed to be null-terminated. Also note that this
/// function will not create a copy of the underlying string data. The data is
/// owned by the `tensor`.
TfLiteStatus TfLiteOpaqueTensorGetString(const TfLiteOpaqueTensor* tensor,
int index, const char** str, int* len);
/// Writes the array of strings specified by `str_array` into
/// the specified `tensor`. The strings provided via the `str_array` are being
/// copied into the `tensor`. Returns `kTfLiteOk` in case of success. Any other
/// return value indicates a failure.
///
/// The provided `str_array_len` must denote the length of `str_array`
/// and `str_n_len[i]` must denote the length of the i-th string.
///
/// The provided strings don't need to be null terminated and may contain
/// embedded null characters. The amount of bytes copied into the `tensor` is
/// entirely determined by `str_n_len[i]` and it is the caller's responsibility
/// to set this value correctly to avoid undefined behavior.
///
/// Also note that calling `TfLiteOpaqueTensorWriteStrings` deallocates any
/// previously stored data in the `tensor`.
TfLiteStatus TfLiteOpaqueTensorWriteStrings(TfLiteOpaqueTensor* tensor,
const char* const* str_array,
int str_array_len,
const int* str_n_len);
/// Writes the string pointed to by the provided `str` pointer of length `len`
/// into the provided `tensor`. The string provided via `str` is
/// copied into the `tensor`. Returns `kTfLiteOk` in case of success. Any
/// other return value indicates a failure.
///
/// Note that calling `TfLiteOpaqueTensorWriteString` deallocates any
/// previously stored data in the `tensor`. E.g. suppose `t` denotes a
/// `TfLiteOpaqueTensor*`, then calling `TfLiteOpaqueTensorWriteString(t, "AB",
/// 2)` followed by a call to `TfLiteOpaqueTensorWriteString(t, "CD", 2)` will
/// lead to `t` containing `CD`, not `ABCD`.
///
/// `TfLiteOpaqueTensorWriteString` is a convenience function for the use case
/// of writing a single string to a tensor and its effects are identical to
/// calling `TfLiteOpaqueTensorWriteStrings` with an array of a single string.
TfLiteStatus TfLiteOpaqueTensorWriteString(TfLiteOpaqueTensor* tensor,
const char* str, int len);
/// An opaque type to create a tensor.
typedef struct TfLiteOpaqueTensorBuilder TfLiteOpaqueTensorBuilder;
/// Creates an opaque tensor builder object.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderCreate();
/// Deletes an opaque tensor builder object.
void TfLiteOpaqueTensorBuilderDelete(TfLiteOpaqueTensorBuilder* builder);
/// Sets the `TfLiteType` of the provided `builder` to the provided `type`.
/// Returns the address of the provided `builder`, so that builder calls can be
/// chained together.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetType(
TfLiteOpaqueTensorBuilder* builder, TfLiteType type);
/// Sets the raw data of the provided `builder` to the provided `data`. Returns
/// the address of the provided `builder`, so that builder calls can be chained
/// together.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetData(
TfLiteOpaqueTensorBuilder* builder, void* data);
/// Sets the allocation type of the provided `builder` to the provided
/// `allocation_type`. The `allocation_type` must be one of the following:
/// `kTfLiteDynamic`, `kTfLiteArenaRw` or `kTfLiteArenaRwPersistent`. If the
/// provided `allocation_type` is not one of those values then
/// `TfLiteOpaqueContextAddTensor` will return an error. Returns the address of
/// the provided `builder`, so that builder calls can be chained together.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetAllocationType(
TfLiteOpaqueTensorBuilder* builder, TfLiteAllocationType allocation_type);
/// Sets the quantization params of the provided `builder` to the provided
/// `params`. Returns the address of the provided `builder`, so that builder
/// calls can be chained together.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetQuantizationParams(
TfLiteOpaqueTensorBuilder* builder, TfLiteQuantizationParams params);
/// Sets the quantization of the provided `builder` to the provided
/// `quantization`. Returns the address of the provided `builder`, so that
/// builder calls can be chained together.
TfLiteOpaqueTensorBuilder* TfLiteOpaqueTensorBuilderSetQuantization(
TfLiteOpaqueTensorBuilder* builder, TfLiteQuantization quantization);
/// Sets the allocation type of the provided `tensor` to `kTfLiteDynamic`.
/// This function has no effect if the `tensor`'s allocation type is already
/// `kTfLiteDynamic`. The provided `tensor` must not be null.
void TfLiteOpaqueTensorSetAllocationTypeToDynamic(TfLiteOpaqueTensor* tensor);
/// Sets the allocation type of the provided `tensor` to `kTfLiteNonCpu`.
/// The provided `tensor` must not be null.
///
/// WARNING: This is an experimental API and subject to change.
void TfLiteOpaqueTensorSetNonCpuAllocation(TfLiteOpaqueTensor* tensor);
// --------------------------------------------------------------------------
// Accessors for TfLiteOpaqueNode.
/// Returns the input tensor of the given node.
TFL_CAPI_EXPORT extern const TfLiteOpaqueTensor* TfLiteOpaqueNodeGetInput(
const TfLiteOpaqueContext* opaque_context,
const TfLiteOpaqueNode* opaque_node, int index);
/// Returns the output tensor of the given node.
TFL_CAPI_EXPORT extern TfLiteOpaqueTensor* TfLiteOpaqueNodeGetOutput(
TfLiteOpaqueContext* opaque_context, const TfLiteOpaqueNode* opaque_node,
int index);
/// Gets the number of input tensors of the provided `opaque_node`.
TFL_CAPI_EXPORT int TfLiteOpaqueNodeNumberOfInputs(
const TfLiteOpaqueNode* opaque_node);
/// Gets the number of output tensors of the provided `opaque_node`.
TFL_CAPI_EXPORT int TfLiteOpaqueNodeNumberOfOutputs(
const TfLiteOpaqueNode* opaque_node);
/// Returns opaque data provided by the node implementer. The value returned
/// from this function is the value that was returned from the `init` callback
/// that was passed to `TfLiteOperatorSetInit`.
TFL_CAPI_EXPORT extern void* TfLiteOpaqueNodeGetUserData(
const TfLiteOpaqueNode* opaque_node);
/// Returns the builtin data associated with the provided `opaque_node`.
///
/// The builtin init data associated with a node would typically be set during
/// the creation of the associated interpreter, through a mechanism like the
/// interpreter builder that loads a TFLite model and initialises the
/// interpreter's nodes accordingly. Under these conditions the returned
/// address remains valid throughout the lifetime of the `opaque_node`.
TFL_CAPI_EXPORT extern void* TfLiteOpaqueNodeGetBuiltinData(
const TfLiteOpaqueNode* opaque_node);
/// Loads into the provided `*init_data` pointer the address of the custom init
/// data associated with the provided `opaque_node`. The length of data is
/// loaded into the provided `size` pointer. Returns `kTfLiteOk` in case
/// of success. Any other return value indicates a failure and will leave
/// `init_data` and `size` in an unspecified state.
///
/// The custom init data associated with a node would typically be set during
/// the creation of the associated interpreter, through a mechanism like the
/// interpreter builder that loads a TFLite model and initialises the
/// interpreter's nodes accordingly. Under these conditions the returned
/// address remains valid throughout the lifetime of the `opaque_node`.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueNodeGetCustomInitialData(
const TfLiteOpaqueNode* opaque_node, const void** init_data, int* size);
/// Loads into the provided `*inputs` pointer the starting address of an array
/// of indices representing the tensors that are inputs of the provided
/// `opaque_node`. The length of the array is loaded into the provided
/// `num_inputs` pointer. Returns `kTfLiteOk` in case of success. Any other
/// return value indicates a failure and will leave `inputs` and
/// `num_inputs` in an unspecified state.
///
/// The input tensors associated with a node would typically be set during the
/// creation of the associated interpreter, through a mechanism like the
/// interpreter builder that loads a TFLite model and initialises the
/// interpreter's nodes accordingly. Under these conditions the loaded address
/// remains valid throughout the lifetime of the `opaque_node`.
TFL_CAPI_EXPORT TfLiteStatus TfLiteOpaqueNodeInputs(
const TfLiteOpaqueNode* opaque_node, const int** inputs, int* num_inputs);
/// Loads into the provided `*outputs` pointer the starting address of an array
/// of indices representing the tensors that are outputs of the provided
/// `opaque_node`. The length of the array is loaded into the provided
/// `num_outputs` pointer. Returns `kTfLiteOk` in case of success. Any other
/// return value indicates a failure and will leave `outputs` and
/// `num_outputs` in an unspecified state.
///
/// The output tensors associated with a node would typically be set during the
/// creation of the associated interpreter, through a mechanism like the
/// interpreter builder that loads a TFLite model and initialises the
/// interpreter's nodes accordingly. Under these conditions the loaded address
/// remains valid throughout the lifetime of the `opaque_node`.
TFL_CAPI_EXPORT TfLiteStatus TfLiteOpaqueNodeOutputs(
const TfLiteOpaqueNode* opaque_node, const int** outputs, int* num_outputs);
/// Set tensor indices of temporary tensors used during the computations.
/// These temporary tensors should be allocated using AddTensors().
/// By default nodes don't have any temporary tensors, tensors, but ops are
/// allowed to change that if they need scratch space of any sort.
/// This will make a copy of the contents of the array pointed to by
/// `temporaries`.
TFL_CAPI_EXPORT TfLiteStatus TfLiteOpaqueNodeSetTemporaries(
TfLiteOpaqueNode* opaque_node, const int* temporaries, int num_temporaries);
/// Loads into the provided `*temporaries` pointer the starting address of an
/// array of indices representing the temporary tensors associated with the
/// provided `opaque_node`. The length of the array is loaded into the provided
/// `num_temporaries` pointer. Returns `kTfLiteOk` in case of success. Any
/// other return value indicates a failure and will leave `temporaries` and
/// `num_temporaries` in an unspecified state.
///
/// The temporary tensors associated with a node would typically be set during
/// the creation of the associated interpreter, through a mechanism like the
/// interpreter builder that loads a TFLite model and initialises the
/// interpreter's nodes accordingly. Under these conditions the loaded address
/// remains valid throughout the lifetime of the `opaque_node`.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueNodeTemporaries(const TfLiteOpaqueNode* opaque_node,
const int** temporaries,
int* num_temporaries);
/// Given an `index_of_input`, which must be in the range of [0, N), where N is
/// the number of input tensors of the provided `opaque_node`, returns the
/// (global) index of the tensor that holds the input. Returns -1 if
/// `index_of_input` is not within the [0, N) range.
TFL_CAPI_EXPORT
int TfLiteOpaqueNodeGetInputTensorIndex(const TfLiteOpaqueNode* opaque_node,
int index_of_input);
/// Given an `index_of_output`, which must be in the range of [0, N), where N is
/// the number of output tensors of the provided `opaque_node`, returns the
/// (global) index of the tensor that holds the output. Returns -1 if
/// `index_of_output` is not within the [0, N) range.
TFL_CAPI_EXPORT
int TfLiteOpaqueNodeGetOutputTensorIndex(const TfLiteOpaqueNode* opaque_node,
int index_of_output);
// --------------------------------------------------------------------------
// Accessors for TfLiteOpaqueContext.
typedef struct TfLiteIntArray TfLiteIntArray;
/// Loads the provided `execution_plan` associated with the provided
/// `opaque_context`. Returns `kTfLiteOk` if the `execution_plan` was
/// successfully loaded. A return value different from `kTfLiteOk` indicates a
/// failure and the `execution_plan` will be left in an unspecified state.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueContextGetExecutionPlan(
TfLiteOpaqueContext* opaque_context, TfLiteIntArray** execution_plan);
/// Returns the external context of the specified type associated with the
/// provided `opaque_context`. Returns `kTfLiteOk` if the external context was
/// successfully loaded. A return value different from `kTfLiteOk` indicates a
/// failure and the `external_context` will be left in an unspecified state.
TFL_CAPI_EXPORT extern TfLiteStatus TfLiteOpaqueContextGetExternalContext(
TfLiteOpaqueContext* opaque_context, void** external_context,
TfLiteExternalContextType type);
/// Given the specified `opaque_context` and `node_index`, load the caller's
/// opaque `*node` and `*registration_external` pointer. Return `kTfLiteOk` if
/// both the `*node` as well as the `*registration_external` have been loaded
/// correctly. Any other return code indicates a failure and both `*node` as
/// well as `*registration_external` will be in an unspecified state.
///
/// A caller can obtain a node's index by calling
/// `TfLiteOpaqueContextGetExecutionPlan`, which provides an array of node
/// indices, sorted in execution order. A node index might also come from the
/// data structures passed to the delegate kernel's callback parameters, like
/// the delegate parameters data structure passed to the `init` callback that
/// contains an array of node indices that are meant to be handled by the
/// delegate kernel.
///
/// This function is expected to be called from within a delegate callback, like
/// `Prepare`, or a delegate kernel callback (i.e., a callback registered with
/// a `TfLiteOperator` object).
///
/// The loaded `*node` and `*registration_external` pointers will generally
/// remain valid for the lifetime of the associated `opaque_context`, but can be
/// invalidated through API calls where delegates get un-applied, like API calls
/// that modify the model graph via a delegate, or if input tensors get
/// re-sized.
///
// TODO(b/237983452): Further clarify the lifetime guarantees of pointers that
// are returned to the users and which actions invalidate them.
TFL_CAPI_EXPORT TfLiteStatus TfLiteOpaqueContextGetNodeAndRegistration(
struct TfLiteOpaqueContext* opaque_context, int node_index,
TfLiteOpaqueNode** node, TfLiteOperator** registration_external);
/// Entry point for C API ReplaceNodeSubsetsWithDelegateKernels
///
/// Replaces the specified `nodes_to_replace` that are associated with the
/// provided `opaque_context` with delegate kernels. The provided
/// `registration_external` represents the delegate kernel and will be used for
/// each node subset that will be delegate to the provided `opaque_delegate`.
///
/// The TF Lite runtime will take ownership of the `registration_external` and
/// will delete it when the associated `opaque_context` gets destroyed.
///
/// The ownership of the `nodes_to_replace` and the `opaque_delegate` remains
/// with the caller.
TFL_CAPI_EXPORT TfLiteStatus
TfLiteOpaqueContextReplaceNodeSubsetsWithDelegateKernels(
struct TfLiteOpaqueContext* opaque_context,
TfLiteOperator* registration_external,
const TfLiteIntArray* nodes_to_replace,
TfLiteOpaqueDelegate* opaque_delegate);
/// Returns modifiable access to the opaque tensor that corresponds to the
/// specified `index` and is associated with the provided `opaque_context`.
///
/// This requires the `index` to be between 0 and N - 1, where N is the
/// number of tensors in the model.
///
/// Typically the tensors associated with the `context` would be set
/// during the initialization of the `interpreter` that the `context` belongs
/// to, through a mechanism like the `InterpreterBuilder`, and remain unchanged
/// throughout the lifetime of the interpreter. However, there are some
/// circumstances in which the pointer may not remain valid throughout the
/// lifetime of the interpreter, because calls to `AddTensors` on the
/// interpreter invalidate the returned pointer.
///
/// The ownership of the tensor remains with the TFLite runtime, meaning the
/// caller should not deallocate the pointer.
TFL_CAPI_EXPORT
TfLiteOpaqueTensor* TfLiteOpaqueContextGetOpaqueTensor(
const TfLiteOpaqueContext* opaque_context, int index);
/// Loads into the provided `*inputs` pointer the starting address of an array
/// of indices representing the tensors that are inputs to the subgraph that is
/// associated with the provided `opaque_context`. The length of the array is
/// loaded into the provided `num_inputs` pointer. Returns `kTfLiteOk` in case
/// of success. Any other return value indicates a failure and will leave
/// `inputs` and `num_inputs` in an unspecified state. Calls to `SetInputs` on
/// the associated subgraph invalidate the loaded pointers.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetInputs(
const struct TfLiteOpaqueContext* opaque_context, const int** inputs,
int* num_inputs);
/// Loads into the provided `*outputs` pointer the starting address of an array
/// of indices representing the tensors that are outputs to the subgraph that is
/// associated with the provided `opaque_context`. The length of the array is
/// loaded into the provided `num_outputs` pointer. Returns `kTfLiteOk` in case
/// of success. Any other return value indicates a failure and will leave
/// `outputs` and `num_outputs` in an unspecified state. Calls to `SetOutputs`
/// on the associated subgraph invalidate the loaded pointers.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetOutputs(
const struct TfLiteOpaqueContext* opaque_context, const int** outputs,
int* num_outputs);
/// Loads into the provided `*variables` pointer the starting address of an
/// array of indices representing the tensors that are variables to the subgraph
/// that is associated with the provided `opaque_context`. The length of the
/// array is loaded into the provided `num_variables` pointer. Returns
/// `kTfLiteOk` in case of success. Any other return value indicates a failure
/// and will leave `variables` and `num_variables` in an unspecified state.
/// Calls to `SetVariables` on the associated subgraph invalidate the loaded
/// pointers.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetVariables(
const struct TfLiteOpaqueContext* opaque_context, const int** variables,
int* num_variables);
/// Returns the number of nodes associated with the provided `opaque_context`.
TFL_CAPI_EXPORT
size_t TfLiteOpaqueContextGetNumNodes(
const struct TfLiteOpaqueContext* opaque_context);
/// Returns the number of tensors associated with the provided `opaque_context`.
TFL_CAPI_EXPORT
size_t TfLiteOpaqueContextGetNumTensors(
const struct TfLiteOpaqueContext* opaque_context);
/// Returns the name of the subgraph that is associated with the provided
/// `opaque_context`. Typically the returned pointer will remain valid
/// throughout the lifetime of the subgraph, but may be invalidated by a call to
/// `Subgraph::SetName`.
TFL_CAPI_EXPORT
const char* TfLiteOpaqueContextGetName(
const struct TfLiteOpaqueContext* opaque_context);
/// Resizes the provided `tensor` that is associated with the provided
/// `context` so that the `tensor`'s shape matches the dimensionality specified
/// via the provided `new_size` array. Returns `kTfLiteOk` in
/// case of success. Any other return value indicates a failure and will leave
/// the `tensor` in an unspecified state. The TF Lite runtime takes ownership
/// of the `new_size` array, even in case of failure.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextResizeTensor(TfLiteOpaqueContext* context,
TfLiteOpaqueTensor* tensor,
TfLiteIntArray* new_size);
/// Entry point for C API AcquireSubgraphContext.
///
/// Retrieves the corresponding TfLiteOpaqueContext of a subgraph given a
/// subgraph index and switches to the delegate context for this subgraph. If an
/// invalid subgraph index is given, then returns kTfLiteError.
///
/// NOTE: This function is expected to be paired with
/// TfLiteOpaqueContextReleaseSubgraphContext() once the delegate preparation is
/// done and/or the delegate context functions are no longer needed.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextAcquireSubgraphContext(
struct TfLiteOpaqueContext* opaque_context, int subgraph_index,
TfLiteOpaqueContext** acquired_opaque_context);
/// Entry point for C API ReleaseSubgraphContext.
///
/// Releases the corresponding TfLiteOpaqueContext by switching back to the
/// TFLite kernel context for this specified subgraph.
///
/// NOTE: This function is expected to be used after
/// TfLiteOpaqueContextAcquireSubgraphContext() once the delegate preparation is
/// done and/or the delegate context functions are no longer needed.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextReleaseSubgraphContext(
struct TfLiteOpaqueContext* opaque_context, int subgraph_index);
/// Entry point for C API MarkSubgraphAsDelegationSkippable
///
/// Marks the subgraph with the given index as "delegation-skippable". Returns
/// kTfLiteOk if the given subgraph index is valid and is successfully marked
/// as delegation-skippable, and an error status if the subgraph index is
/// invalid.
/// If a subgraph is delegation-skippable, then the subgraph will be handled by
/// a specific TfLiteOpaqueDelegate that is already supposed to be
/// aware of this condition, and therefore, TfLiteInterpreter can skip invoking
/// `ModifyGraphWithDelegate` on this subgraph.
///
/// NOTE: This function is expected to be called only when the subgraph that
/// `subgraph_index` is pointing to should be skipped by
/// interpreter::ModifyGraphWithDelegate (e.g. the subgraph is part of the list
/// of callee subgraphs of the same control flow node, and all of those callees
/// are supported by the same delegate at once).
///
/// For example, this function can be used when the delegate is handling
/// control flow ops such as while ops. For instance, a while op has a condition
/// subgraph indexed at `i` and a body subgraph indexed at `j`. The op can be
/// delegated when the following conditions hold:
/// 1. The delegate supports while op
/// 2. Both condition subgraph `i` and body subgraph `j` can be fully
/// delegated to the delegate.
///
/// Then if the delegate decides to support the while node along with both body
/// and condition subgraphs, it should mark subgraphs `i` and `j` skippable so
/// that those two subgraphs won't be delegated to another delegate.
///
/// WARNING: It is the delegate's responsibility to define when to skip
/// `Subgraph::ModifyGraphWithDelegate`, to check for any edge cases (i.e.
/// multiple references to the subgraph that `subgraph_index` is pointing to),
/// and to mark a subgraph as skippable by using this function.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextMarkSubgraphAsDelegationSkippable(
TfLiteOpaqueContext* opaque_context, int subgraph_index);
/// Loads metadata of a TF Lite node's custom initialization data. Specifically:
/// * Loads into the supplied `fd` the file descriptor of the file that stores
/// the `node`'s custom initialization data. This output parameter will be
/// loaded if the TF Lite runtime has access to the file descriptor, though
/// this is not always the case, e.g. if a client provides a tflite::Model
/// directly to the TF Lite runtime. If `fd` can be loaded then `kTfLiteOk`
/// will be returned, otherwise `kTfLiteError` is returned.
/// * Loads into the supplied `custom_initial_data_offset_in_file` pointer the
/// offset of the `node`'s custom init data in the file associated with `fd`.
/// This output parameter will be set to -1 if the `node` does not have custom
/// init data set.
/// * Loads into the supplied `custom_initial_data_size` the size of the
/// custom initialization data. This output parameter will be set to -1 if
/// the `node` does not have custom init data set.
///
/// Returns `kTfLiteOk` when `fd` has been loaded successfully and
/// `kTfLiteError` otherwise. Note that this means that `kTfLiteOk` can be
/// returned, even if the `node` does not have custom init data set.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetNodeInitDataMmapInfo(
const TfLiteOpaqueContext* context, const TfLiteOpaqueNode* node, int* fd,
int64_t* custom_initial_data_offset_in_file,
int64_t* custom_initial_data_size);
/// Adds an additional tensor and configures its properties based on the
/// provided `builder`, preserving pre-existing Tensor entries. If non-null,
/// the value pointed to by `new_tensor_index` will be set to the index of the
/// new tensor. Returns `kTfLiteOk` when the tensor has been added
/// successfully. Returns `kTfLiteError` in case of failure.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextAddTensor(TfLiteOpaqueContext* context,
TfLiteOpaqueTensorBuilder* builder,
int* new_tensor_index);
/// Populates the size in bytes of a provide `type` into `bytes`. Returns
/// `kTfLiteOk` for valid types, and `kTfLiteError` otherwise.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetSizeOfType(TfLiteOpaqueContext* context,
TfLiteType type, size_t* bytes);
/// Retrieves named metadata buffer from the TFLite model.
/// Returns kTfLiteOk if metadata is successfully obtained from the flatbuffer
/// model. That is, there exists a `metadata` entry with given `name` string.
/// (see TFLite's schema.fbs).
/// The corresponding `buffer` information is populated in `ptr` & `bytes`.
/// The data from `ptr` is valid for the lifetime of the Interpreter.
TFL_CAPI_EXPORT
TfLiteStatus TfLiteOpaqueContextGetMetadata(TfLiteOpaqueContext* context,
const char* name, const char** ptr,
size_t* bytes);
/// Reports an error message formed by using the provided `format` string in
/// combination with the data provided via the unnamed arguments following
/// the `format` parameter (`...`). The intended usage and behavior is the same
/// as with `printf` with regards to how the data and the formatting string
/// interact. E.g.
/// `TfLiteOpaqueContextReportError(opaque_context, "a=%d b=%d", a, b);`
///
/// The provided `opaque_context` will be used for reporting the resulting error
/// message.
///
/// Note that TF Lite clients can use macros like `TF_LITE_OPAQUE_ENSURE` to
/// check for certain conditions to be true, and print an error message if the
/// condition does not hold. Direct usage of this function from application
/// code should therefore be rare.
TFL_CAPI_EXPORT
void TfLiteOpaqueContextReportError(struct TfLiteOpaqueContext* opaque_context,
const char* format, ...);
/// Same as `TfLiteOpaqueContextReportError`, but with the variable arguments
/// passed via a `va_list` instead of directly.
///
/// Callers that receive an ellipsis and want to forward it to
/// to the opaque context error reporting API can add the ellipsis content to a
/// `va_list` and then call `TfLiteOpaqueContextReportErrorVa`. E.g.:
///
///
/// void MyErrorReporter(struct TfLiteOpaqueContext* opaque_context,
/// const char* format, ...) {
/// va_list vlist;
/// va_start(vlist, format);
/// TfLiteOpaqueContextReportErrorVa(opaque_context, format, vlist);
/// va_end(vlist);
/// }
TFL_CAPI_EXPORT
void TfLiteOpaqueContextReportErrorVa(
struct TfLiteOpaqueContext* opaque_context, const char* format,
va_list vlist);
// Since we must not depend on any libraries, define a minimal subset of
// error macros while avoiding names that have pre-conceived meanings like
// assert and check.
// Try to make all reporting calls through TF_LITE_OPAQUE_KERNEL_LOG rather than
// calling the TfLiteOpaqueContextReportError function directly, so that message
// strings can be stripped out if the binary size needs to be severely
// optimized.
#ifndef TF_LITE_STRIP_ERROR_STRINGS
#if !defined(TF_LITE_OPAQUE_KERNEL_LOG)
#define TF_LITE_OPAQUE_KERNEL_LOG(opaque_context, ...) \
do { \
TfLiteOpaqueContextReportError((opaque_context), __VA_ARGS__); \
} while (false)
#endif
#if !defined(TF_LITE_OPAQUE_MAYBE_KERNEL_LOG)
#define TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(opaque_context, ...) \
do { \
if ((opaque_context) != nullptr) { \
TfLiteOpaqueContextReportError((opaque_context), __VA_ARGS__); \
} \
} while (false)
#endif
#else // TF_LITE_STRIP_ERROR_STRINGS
#define ARGS_UNUSED(...) (void)sizeof(#__VA_ARGS__)
#if !defined(TF_LITE_OPAQUE_MAYBE_KERNEL_LOG)
#define TF_LITE_OPAQUE_KERNEL_LOG(opaque_context, ...) ARGS_UNUSED(__VA_ARGS__)
#endif
#if !defined(TF_LITE_OPAQUE_MAYBE_KERNEL_LOG)
#define TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(opaque_context, ...) \
ARGS_UNUSED(__VA_ARGS__)
#endif
#endif // TF_LITE_STRIP_ERROR_STRINGS
/// Check whether value is true, and if not return kTfLiteError from
/// the current function (and report the error string msg).
#if !defined(TF_LITE_OPAQUE_ENSURE_MSG)
#define TF_LITE_OPAQUE_ENSURE_MSG(opaque_context, value, msg) \
do { \
if (!(value)) { \
TF_LITE_OPAQUE_KERNEL_LOG((opaque_context), __FILE__ " " msg); \
return kTfLiteError; \
} \
} while (0)
#endif
/// Check whether the value `a` is true, and if not return kTfLiteError from
/// the current function, while also reporting the location of the error.
#if !defined(TF_LITE_OPAQUE_ENSURE)
#define TF_LITE_OPAQUE_ENSURE(opaque_context, a) \
do { \
if (!(a)) { \
TF_LITE_OPAQUE_KERNEL_LOG(opaque_context, "%s:%d: %s was not true.", \
__FILE__, __LINE__, #a); \
return kTfLiteError; \
} \
} while (0)
#endif
/// Check whether the value `a == b` is true, and if not return kTfLiteError
/// from the current function, while also reporting the location of the error.
/// `a` and `b` may be evaluated more than once, so no side effects or
/// extremely expensive computations should be done.
///
/// NOTE: Use TF_LITE_ENSURE_TYPES_EQ if comparing TfLiteTypes.
#if !defined(TF_LITE_OPAQUE_ENSURE_EQ)
#define TF_LITE_OPAQUE_ENSURE_EQ(opaque_context, a, b) \
do { \
if ((a) != (b)) { \
TF_LITE_OPAQUE_KERNEL_LOG((opaque_context), \
"%s:%d: %s != %s (%d != %d)", __FILE__, \
__LINE__, #a, #b, (a), (b)); \
return kTfLiteError; \
} \
} while (0)
#endif
#if !defined(TF_LITE_OPAQUE_ENSURE_TYPES_EQ)
#define TF_LITE_OPAQUE_ENSURE_TYPES_EQ(opaque_context, a, b) \
do { \
if ((a) != (b)) { \
TF_LITE_OPAQUE_KERNEL_LOG( \
(opaque_context), "%s:%d: %s != %s (%s != %s)", __FILE__, __LINE__, \
#a, #b, TfLiteTypeGetName(a), TfLiteTypeGetName(b)); \
return kTfLiteError; \
} \
} while (0)
#endif
#if !defined(TF_LITE_OPAQUE_ENSURE_NEAR)
#define TF_LITE_OPAQUE_ENSURE_NEAR(opaque_context, a, b, epsilon) \
do { \
double delta = ((a) > (b)) ? ((a) - (b)) : ((b) - (a)); \
if (delta > epsilon) { \
TF_LITE_OPAQUE_KERNEL_LOG((opaque_context), \
"%s:%d: %s not near %s (%f != %f)", __FILE__, \
__LINE__, #a, #b, (double)(a), (double)(b)); \
return kTfLiteError; \
} \
} while (0)
#endif
#ifndef TF_LITE_STATIC_MEMORY
/// Creates an opaque delegate and returns its address. The opaque delegate
/// will behave according to the provided `opaque_delegate_builder`. The
/// lifetime of the objects pointed to by any of the fields within the
/// `opaque_delegate_builder` must outlive the returned
/// `TfLiteOpaqueDelegate` and any `TfLiteInterpreter`,
/// `TfLiteInterpreterOptions`, `tflite::Interpreter`, or
/// `tflite::InterpreterBuilder` that the delegate is added to. The returned
/// address should be passed to `TfLiteOpaqueDelegateDelete` for deletion. If
/// `opaque_delegate_builder` is a null pointer, then a null pointer will be
/// returned.
TfLiteOpaqueDelegate* TfLiteOpaqueDelegateCreate(
const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder);
/// Deletes the provided opaque `delegate`. This function has no effect if the
/// `delegate` is a null pointer.
void TfLiteOpaqueDelegateDelete(TfLiteOpaqueDelegate* delegate);
#endif // TF_LITE_STATIC_MEMORY
/// Returns a pointer to the data associated with the provided opaque
/// `delegate`.
///
/// A null pointer will be returned when:
/// - The `delegate` is null.
/// - The `data` field of the `TfLiteOpaqueDelegateBuilder` used to construct
/// the `delegate` was null.
/// - Or in case of any other error.
/// - The `delegate` has been constructed via a `TfLiteOpaqueDelegateBuilder`,
/// but the `data` field of the `TfLiteOpaqueDelegateBuilder` is null.
///
/// The data_ field of `delegate` will be returned if the
/// `opaque_delegate_builder` field is null.
void* TfLiteOpaqueDelegateGetData(const TfLiteOpaqueDelegate* delegate);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
/** @} */
#endif // TENSORFLOW_LITE_CORE_C_C_API_OPAQUE_H_

Some files were not shown because too many files have changed in this diff Show More