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
+25
View File
@@ -0,0 +1,25 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("//tensorflow/compiler/mlir/lite:build_def.bzl", "tflite_copts")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
# LINT.IfChange(command_line_flags)
cc_library(
name = "command_line_flags",
srcs = ["command_line_flags.cc"],
hdrs = ["command_line_flags.h"],
copts = tflite_copts(),
deps = [
"@com_google_absl//absl/log",
"@com_google_absl//absl/strings",
],
)
# LINT.ThenChange(//tensorflow/lite/tools:command_line_flags)
@@ -0,0 +1,250 @@
/* 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/compiler/mlir/lite/tools/command_line_flags.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <functional>
#include <numeric>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/log/log.h"
#include "absl/strings/match.h"
// TODO(b/321735756): Remove this file and copy original once common library is
// implemented.
// LINT.IfChange
namespace mlir {
namespace {
template <typename T>
std::string ToString(T val) {
std::ostringstream stream;
stream << val;
return stream.str();
}
template <>
std::string ToString(bool val) {
return val ? "true" : "false";
}
template <>
std::string ToString(const std::string& val) {
return val;
}
bool ParseFlag(const std::string& arg, int argv_position,
const std::string& flag, bool positional,
const std::function<bool(const std::string&, int argv_position)>&
parse_func,
bool* value_parsing_ok) {
if (positional) {
*value_parsing_ok = parse_func(arg, argv_position);
return true;
}
*value_parsing_ok = true;
std::string flag_prefix = "--" + flag + "=";
if (!absl::StartsWith(arg, flag_prefix)) {
return false;
}
bool has_value = arg.size() >= flag_prefix.size();
*value_parsing_ok = has_value;
if (has_value) {
*value_parsing_ok =
parse_func(arg.substr(flag_prefix.size()), argv_position);
}
return true;
}
template <typename T>
bool ParseFlag(const std::string& flag_value, int argv_position,
const std::function<void(const T&, int)>& hook) {
std::istringstream stream(flag_value);
T read_value;
stream >> read_value;
if (!stream.eof() && !stream.good()) {
return false;
}
hook(read_value, argv_position);
return true;
}
template <>
bool ParseFlag(const std::string& flag_value, int argv_position,
const std::function<void(const bool&, int)>& hook) {
if (flag_value != "true" && flag_value != "false" && flag_value != "0" &&
flag_value != "1") {
return false;
}
hook(flag_value == "true" || flag_value == "1", argv_position);
return true;
}
template <typename T>
bool ParseFlag(const std::string& flag_value, int argv_position,
const std::function<void(const std::string&, int)>& hook) {
hook(flag_value, argv_position);
return true;
}
} // namespace
#define CONSTRUCTOR_IMPLEMENTATION(flag_T, default_value_T, flag_enum_val) \
Flag::Flag(const char* name, \
const std::function<void(const flag_T& /*flag_val*/, \
int /*argv_position*/)>& hook, \
default_value_T default_value, const std::string& usage_text, \
FlagType flag_type) \
: name_(name), \
type_(flag_enum_val), \
value_hook_([hook](const std::string& flag_value, int argv_position) { \
return ParseFlag<flag_T>(flag_value, argv_position, hook); \
}), \
default_for_display_(ToString<default_value_T>(default_value)), \
usage_text_(usage_text), \
flag_type_(flag_type) {}
CONSTRUCTOR_IMPLEMENTATION(int32_t, int32_t, TYPE_INT32)
CONSTRUCTOR_IMPLEMENTATION(int64_t, int64_t, TYPE_INT64)
CONSTRUCTOR_IMPLEMENTATION(float, float, TYPE_FLOAT)
CONSTRUCTOR_IMPLEMENTATION(bool, bool, TYPE_BOOL)
CONSTRUCTOR_IMPLEMENTATION(std::string, const std::string&, TYPE_STRING)
#undef CONSTRUCTOR_IMPLEMENTATION
bool Flag::Parse(const std::string& arg, int argv_position,
bool* value_parsing_ok) const {
return ParseFlag(
arg, argv_position, name_, flag_type_ == kPositional,
[&](const std::string& read_value, int argv_position) {
return value_hook_(read_value, argv_position);
},
value_parsing_ok);
}
/*static*/ bool Flags::Parse(int* argc, const char** argv,
const std::vector<Flag>& flag_list) {
bool result = true;
std::vector<bool> unknown_argvs(*argc, true);
// Record the list of flags that have been processed. key is the flag's name
// and the value is the corresponding argv index if there's one, or -1 when
// the argv list doesn't contain this flag.
std::unordered_map<std::string, int> processed_flags;
// Stores indexes of flag_list in a sorted order.
std::vector<int> sorted_idx(flag_list.size());
std::iota(std::begin(sorted_idx), std::end(sorted_idx), 0);
std::sort(sorted_idx.begin(), sorted_idx.end(), [&flag_list](int a, int b) {
return flag_list[a].GetFlagType() < flag_list[b].GetFlagType();
});
int positional_count = 0;
for (int idx = 0; idx < sorted_idx.size(); ++idx) {
const Flag& flag = flag_list[sorted_idx[idx]];
const auto it = processed_flags.find(flag.name_);
if (it != processed_flags.end()) {
#ifndef NDEBUG
// Only log this in debug builds.
LOG(WARNING) << "Duplicate flags: " << flag.name_;
#endif
if (it->second != -1) {
bool value_parsing_ok;
flag.Parse(argv[it->second], it->second, &value_parsing_ok);
if (!value_parsing_ok) {
LOG(ERROR) << "Failed to parse flag '" << flag.name_
<< "' against argv '" << argv[it->second] << "'";
result = false;
}
continue;
} else if (flag.flag_type_ == Flag::kRequired) {
LOG(ERROR) << "Required flag not provided: " << flag.name_;
// If the required flag isn't found, we immediately stop the whole flag
// parsing.
result = false;
break;
}
}
// Parses positional flags.
if (flag.flag_type_ == Flag::kPositional) {
if (++positional_count >= *argc) {
LOG(ERROR) << "Too few command line arguments.";
return false;
}
bool value_parsing_ok;
flag.Parse(argv[positional_count], positional_count, &value_parsing_ok);
if (!value_parsing_ok) {
LOG(ERROR) << "Failed to parse positional flag: " << flag.name_;
return false;
}
unknown_argvs[positional_count] = false;
processed_flags[flag.name_] = positional_count;
continue;
}
// Parse other flags.
bool was_found = false;
for (int i = positional_count + 1; i < *argc; ++i) {
if (!unknown_argvs[i]) continue;
bool value_parsing_ok;
was_found = flag.Parse(argv[i], i, &value_parsing_ok);
if (!value_parsing_ok) {
LOG(ERROR) << "Failed to parse flag '" << flag.name_
<< "' against argv '" << argv[i] << "'";
result = false;
}
if (was_found) {
unknown_argvs[i] = false;
processed_flags[flag.name_] = i;
break;
}
}
// If the flag is found from the argv (i.e. the flag name appears in argv),
// continue to the next flag parsing.
if (was_found) continue;
// The flag isn't found, do some bookkeeping work.
processed_flags[flag.name_] = -1;
if (flag.flag_type_ == Flag::kRequired) {
LOG(ERROR) << "Required flag not provided: " << flag.name_;
result = false;
// If the required flag isn't found, we immediately stop the whole flag
// parsing by breaking the outer-loop (i.e. the 'sorted_idx'-iteration
// loop).
break;
}
}
int dst = 1; // Skip argv[0]
for (int i = 1; i < *argc; ++i) {
if (unknown_argvs[i]) {
argv[dst++] = argv[i];
}
}
*argc = dst;
return result && (*argc < 2 || std::strcmp(argv[1], "--help") != 0);
}
} // namespace mlir
// LINT.ThenChange(//tensorflow/lite/tools/command_line_flags.cc)
@@ -0,0 +1,170 @@
/* 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_COMPILER_MLIR_LITE_TOOLS_COMMAND_LINE_FLAGS_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_COMMAND_LINE_FLAGS_H_
#include <functional>
#include <string>
#include <vector>
// TODO(b/321735756): Remove this file once common library is implemented with
// the originial file.
// LINT.IfChange
namespace mlir {
// A simple command-line argument parsing module.
// Dependency free simplified port of core/util/command_line_flags.
// This class is written for benchmarks and uses inefficient string
// concatenation. This was written to avoid dependency on tensorflow/core/util
// which transitively brings in a lot of other dependencies that are not
// necessary for tflite benchmarking code.
// The recommended way of using it is with local variables and an initializer
// list of Flag objects, for example:
//
// int some_int = 10;
// bool some_switch = false;
// std::string some_name = "something";
//
// std::vector<tensorFlow::Flag> flag_list = {
// Flag::CreateFlag("some_int", &some_int, "an integer that affects X"),
// Flag::CreateFlag("some_switch", &some_switch, "a bool that affects Y"),
// Flag::CreateFlag("some_name", &some_name, "a string that affects Z")
// };
// // Get usage message before ParseFlags() to capture default values.
// std::string usage = Flag::Usage(argv[0], flag_list);
// bool parsed_values_ok = Flags::Parse(&argc, argv, flag_list);
//
// tensorflow::port::InitMain(usage.c_str(), &argc, &argv);
// if (argc != 1 || !parsed_values_ok) {
// ...output usage and error message...
// }
//
// The argc and argv values are adjusted by the Parse function so all that
// remains is the program name (at argv[0]) and any unknown arguments fill the
// rest of the array. This means you can check for flags that weren't understood
// by seeing if argv is greater than 1.
// The result indicates if there were any errors parsing the values that were
// passed to the command-line switches. For example, --some_int=foo would return
// false because the argument is expected to be an integer.
//
// NOTE: Unlike gflags-style libraries, this library is intended to be
// used in the `main()` function of your binary. It does not handle
// flag definitions that are scattered around the source code.
// A description of a single command line flag, holding its name, type, usage
// text, and a pointer to the corresponding variable.
class Flag {
public:
enum FlagType {
kPositional = 0,
kRequired,
kOptional,
};
// The order of the positional flags is the same as they are added.
// Positional flags are supposed to be required.
template <typename T>
static Flag CreateFlag(const char* name, T* val, const char* usage,
FlagType flag_type = kOptional) {
return Flag(
name, [val](const T& v) { *val = v; }, *val, usage, flag_type);
}
// "flag_T" is same as "default_value_T" for trivial types, like int32, bool
// etc. But when it's a complex type, "default_value_T" is generally a const
// reference "flag_T".
#define CONSTRUCTOR_WITH_ARGV_INDEX(flag_T, default_value_T) \
Flag(const char* name, \
const std::function<void(const flag_T& /*flag_val*/, \
int /*argv_position*/)>& hook, \
default_value_T default_value, const std::string& usage_text, \
FlagType flag_type);
#define CONSTRUCTOR_WITHOUT_ARGV_INDEX(flag_T, default_value_T) \
Flag(const char* name, const std::function<void(const flag_T&)>& hook, \
default_value_T default_value, const std::string& usage_text, \
FlagType flag_type) \
: Flag( \
name, [hook](const flag_T& flag_val, int) { hook(flag_val); }, \
default_value, usage_text, flag_type) {}
CONSTRUCTOR_WITH_ARGV_INDEX(int32_t, int32_t)
CONSTRUCTOR_WITHOUT_ARGV_INDEX(int32_t, int32_t)
CONSTRUCTOR_WITH_ARGV_INDEX(int64_t, int64_t)
CONSTRUCTOR_WITHOUT_ARGV_INDEX(int64_t, int64_t)
CONSTRUCTOR_WITH_ARGV_INDEX(float, float)
CONSTRUCTOR_WITHOUT_ARGV_INDEX(float, float)
CONSTRUCTOR_WITH_ARGV_INDEX(bool, bool)
CONSTRUCTOR_WITHOUT_ARGV_INDEX(bool, bool)
CONSTRUCTOR_WITH_ARGV_INDEX(std::string, const std::string&)
CONSTRUCTOR_WITHOUT_ARGV_INDEX(std::string, const std::string&)
#undef CONSTRUCTOR_WITH_ARGV_INDEX
#undef CONSTRUCTOR_WITHOUT_ARGV_INDEX
FlagType GetFlagType() const { return flag_type_; }
private:
friend class Flags;
bool Parse(const std::string& arg, int argv_position,
bool* value_parsing_ok) const;
std::string name_;
enum {
TYPE_INT32,
TYPE_INT64,
TYPE_BOOL,
TYPE_STRING,
TYPE_FLOAT,
} type_;
std::function<bool(const std::string& /*read_value*/, int /*argv_position*/)>
value_hook_;
std::string default_for_display_;
std::string usage_text_;
FlagType flag_type_;
};
class Flags {
public:
// Parse the command line represented by argv[0, ..., (*argc)-1] to find flag
// instances matching flags in flaglist[]. Update the variables associated
// with matching flags, and remove the matching arguments from (*argc, argv).
// Return true iff all recognized flag values were parsed correctly, and the
// first remaining argument is not "--help".
// Note:
// 1. when there are duplicate args in argv for the same flag, the flag value
// and the parse result will be based on the 1st arg.
// 2. when there are duplicate flags in flag_list (i.e. two flags having the
// same name), all of them will be checked against the arg list and the parse
// result will be false if any of the parsing fails.
// See *Duplicate* unit tests in command_line_flags_test.cc for the
// illustration of such behaviors.
static bool Parse(int* argc, const char** argv,
const std::vector<Flag>& flag_list);
};
} // namespace mlir
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_COMMAND_LINE_FLAGS_H_
// LINT.ThenChange(//tensorflow/lite/tools/command_line_flags.h)
@@ -0,0 +1,39 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
load(
"//tensorflow/compiler/mlir/lite:build_def.bzl",
"tflite_copts",
)
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = [
"//visibility:public",
],
licenses = ["notice"],
)
cc_library(
name = "reduced_precision_metadata",
srcs = [],
hdrs = [
"reduced_precision_metadata.h",
],
compatible_with = get_compatible_with_portable(),
visibility = ["//visibility:public"],
deps = [
"//tensorflow/compiler/mlir/lite/kernels/internal:compatibility_macros",
],
)
cc_library(
name = "operator_property",
srcs = ["operator_property.cc"],
hdrs = ["operator_property.h"],
compatible_with = get_compatible_with_portable(),
copts = tflite_copts(),
deps = [
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/compiler/mlir/lite/schema:schema_utils",
],
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
/* 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_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_OPERATOR_PROPERTY_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_OPERATOR_PROPERTY_H_
#include <cassert>
#include <functional>
#include <initializer_list>
#include <utility>
#include <vector>
#include "tensorflow/compiler/mlir/lite/schema/schema_generated.h"
namespace tflite {
namespace optimize {
namespace operator_property {
// The scales of a certain tensor can be derived from the multiplications of all
// the scales. For example, for bias in conv, derived_scale = {{0, 1}, {}, {}}
// and for lstm gate bias, the derived scale is {{}, {0}, {2^-10}}
struct DerivedScale {
// MSVC2015 version 14.0 and below doesn't support struct initialization with
// initializer lists so emulate the behavior using a float initializer list.
#if _MSC_VER <= 1900
DerivedScale() = default;
// Construct this object with a list of initializer lists. All list elements
// are cast to float values to avoid ambiguous construction of a union-style
// object that could take either std::initializer_list<float> or
// std::initializer_list<int>.
DerivedScale(std::initializer_list<std::initializer_list<float>> values) {
assert(values.size() == 3);
std::vector<std::initializer_list<float>> items(values);
for (auto& it : items[0]) {
input_tensors.push_back(static_cast<int>(it));
}
for (auto& it : items[1]) {
intermediate_tensors.push_back(static_cast<int>(it));
}
factors.assign(items[2]);
}
#endif // _MSC_VER <= 1900
std::vector<int> input_tensors = {};
std::vector<int> intermediate_tensors = {};
// This is a list of extra factors that are not associated with any other
// tensor.
std::vector<float> factors = {};
};
struct TensorProperty {
// per_axis also implies symmetric currently.
bool per_axis = false;
// TODO(jianlijianli): remove dimension index and read it from tensor instead.
int per_axis_index = 0;
bool symmetric = false;
// Constraints.
bool restriction = false;
// scale/zero_point hardcoded.
std::pair<float, int> restricted_value_int8 = {0.0f, 0};
std::pair<float, int> restricted_value_int16 = {0.0f, 0};
// Use derived scale.
bool use_derived_scale = false;
// The derived scale.
DerivedScale derived_scale;
// The number of bits for this tensor. It could be 8, 16, 32 or even not power
// of two.
int number_of_bits = 8;
// Extend the range to power of two.
bool extend_to_power_of_two = false;
// State tensor.
bool state_tensor = false;
};
struct OperatorProperty {
// Is a quantized operations currently supported.
bool quantizable = true;
// Is a quantized operations currently supported for 16x8
bool quantizable_int16 = true;
// Op has arbitrary number of inputs, such as concat.
bool arbitrary_inputs = false;
// Op has arbitrary number of outputs, such as slice.
bool arbitrary_outputs = false;
// Input indexes -> input tensor property.
// Must be topologically sorted since there are derived scales.
std::vector<std::pair<int, TensorProperty>> inputs = {};
// Output indexes -> output tensor property.
std::vector<std::pair<int, TensorProperty>> outputs = {};
// Bias indexes.
// TODO(jianlijianli): remove this by putting biases into inputs as well since
// we now can model "derived scale".
std::vector<int> biases = {};
// Intermediate indexes -> intermediate tensor property.
std::vector<std::pair<int, TensorProperty>> intermediates = {};
// Force output to reuse the same scale and zero point of input when the
// certain type support must require the same scale and zero point
// requirement.
std::function<bool(TensorType)> restrict_same_input_output_scale =
[](TensorType) { return false; };
// Use same min of min and max of max for each group.
// Incompatible with restrict_same_input_output_scale and restricted_value.
// Currently it only supports scale pair of {input_index, output_index}.
std::vector<std::vector<int>> restrict_scale = {};
// Op version.
int version = 1;
// When we quantize activations into 16 bit and weights into 8 bit,
// we want to quantize all inputs, including constant tensors,
// for the operators like Add, Mul into 16-bit as well. The constant
// inputs are quantized as weights and this variable indicates
// that we want to do quantizations of these tensors as activations.
bool quantize_input_as_activations = false;
};
// The op as well as it variants.
struct OpVariant {
BuiltinOperator op_code;
bool use_layer_norm = false;
bool use_projection = false;
bool use_peephole = false;
// An attribute to indicate if quantization is supported for this Op.
// This attribute is equivalent to the "quantizable" attribute in
// "OperatorProperty". It added here since OpVariants peeks inside the Op and
// determines its quantization related properties.
bool is_quantizable = true;
};
OperatorProperty GetOperatorProperty(const ModelT* model, int subgraph_index,
int op_index, int number_of_bits = 8);
OperatorProperty GetOperatorProperty(OpVariant op_variant,
int number_of_bits = 8);
} // namespace operator_property
} // namespace optimize
} // namespace tflite
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_OPERATOR_PROPERTY_H_
@@ -0,0 +1,119 @@
/* Copyright 2024 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_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_REDUCED_PRECISION_METADATA_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_REDUCED_PRECISION_METADATA_H_
#include <cstdint>
#include <cstdlib>
#include <string>
#include <utility>
#include "tensorflow/compiler/mlir/lite/kernels/internal/compatibility_macros.h"
namespace tflite {
namespace optimize {
static constexpr char kTfLiteReducedPrecisionKey[] =
"reduced_precision_support";
static constexpr char kTfLiteFloat16String[] = "fp16";
static constexpr char kTfLiteBfloat16String[] = "bf16";
static constexpr char kTfLiteFloat32String[] = "fp32";
static constexpr char kTfLiteAccumulationString[] = "acc";
enum class ReducedPrecisionSupport : std::uint8_t {
None = 0,
Float16Inference = 0x1,
Bfloat16Inference = 0x2,
Float16Accumulation = 0x4,
Float32Accumulation = 0x8,
};
inline ReducedPrecisionSupport operator|(ReducedPrecisionSupport a,
ReducedPrecisionSupport b) {
return static_cast<ReducedPrecisionSupport>(static_cast<std::uint32_t>(a) |
static_cast<std::uint32_t>(b));
}
inline ReducedPrecisionSupport& operator|=(ReducedPrecisionSupport& a,
ReducedPrecisionSupport b) {
return a = static_cast<ReducedPrecisionSupport>(
static_cast<std::uint32_t>(a) | static_cast<std::uint32_t>(b));
}
inline ReducedPrecisionSupport operator&(ReducedPrecisionSupport a,
ReducedPrecisionSupport b) {
return static_cast<ReducedPrecisionSupport>(static_cast<std::uint32_t>(a) &
static_cast<std::uint32_t>(b));
}
inline ReducedPrecisionSupport& operator&=(ReducedPrecisionSupport& a,
ReducedPrecisionSupport b) {
return a = static_cast<ReducedPrecisionSupport>(
static_cast<std::uint32_t>(a) & static_cast<std::uint32_t>(b));
}
inline bool SupportsFP16Inference(const ReducedPrecisionSupport& mask) {
return static_cast<bool>(mask & ReducedPrecisionSupport::Float16Inference);
}
inline bool SupportsBfloat16Inference(const ReducedPrecisionSupport& mask) {
return static_cast<bool>(mask & ReducedPrecisionSupport::Bfloat16Inference);
}
inline bool SupportsFP16Accumulation(const ReducedPrecisionSupport& mask) {
return static_cast<bool>(mask & ReducedPrecisionSupport::Float16Accumulation);
}
inline bool SupportsFP32Accumulation(const ReducedPrecisionSupport& mask) {
return static_cast<bool>(mask & ReducedPrecisionSupport::Float32Accumulation);
}
inline bool SupportsReducedPrecisionInference(
const ReducedPrecisionSupport& mask) {
return SupportsFP16Inference(mask) || SupportsBfloat16Inference(mask);
}
inline bool SupportsEitherFP16OrFP32Accumulation(
const ReducedPrecisionSupport& mask) {
return SupportsFP16Accumulation(mask) != SupportsFP32Accumulation(mask);
}
// Return the key-value pair for reduced precision support metadata.
// Example: mask = Float16Inference | Bfloat16Inference | Float32Accumulation;
// Returned value would be <"reduced_precision_support", "fp16bf16accfp32">.
inline std::pair<std::string, std::string> MetadataForReducedPrecisionSupport(
const ReducedPrecisionSupport& mask) {
TFLITE_DCHECK(SupportsReducedPrecisionInference(mask));
TFLITE_DCHECK(SupportsEitherFP16OrFP32Accumulation(mask));
std::string value = "";
if (SupportsFP16Inference(mask)) {
value += kTfLiteFloat16String;
}
if (SupportsBfloat16Inference(mask)) {
value += kTfLiteBfloat16String;
}
value += kTfLiteAccumulationString;
if (SupportsFP16Accumulation(mask)) {
value += kTfLiteFloat16String;
} else if (SupportsFP32Accumulation(mask)) {
value += kTfLiteFloat32String;
}
return std::make_pair(std::string(kTfLiteReducedPrecisionKey), value);
}
} // namespace optimize
} // namespace tflite
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_OPTIMIZE_REDUCED_PRECISION_METADATA_H_
@@ -0,0 +1,99 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load(
"//tensorflow:tensorflow.bzl",
"tf_cc_test",
)
load("//tensorflow:tensorflow.default.bzl", "get_compatible_with_portable")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:LICENSE"],
default_visibility = ["//visibility:public"],
licenses = ["notice"],
)
cc_library(
name = "versioning",
srcs = [
"op_version.cc",
"runtime_version.cc",
],
hdrs = [
"op_version.h",
"runtime_version.h",
],
compatible_with = get_compatible_with_portable(),
deps = [
":op_signature",
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
"//tensorflow/compiler/mlir/lite/kernels/internal:compatibility_macros",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs_with_mutable",
"//tensorflow/compiler/mlir/lite/schema:schema_utils",
"@com_google_absl//absl/log",
"@com_google_absl//absl/strings",
"@flatbuffers",
],
)
tf_cc_test(
name = "op_version_test",
srcs = [
"op_version_test.cc",
],
deps = [
":op_signature",
":versioning",
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs_with_mutable",
"@com_google_googletest//:gtest_main",
],
)
tf_cc_test(
name = "runtime_version_test",
srcs = [
"runtime_version_test.cc",
],
deps = [
":versioning",
"@com_google_googletest//:gtest_main",
],
)
cc_library(
name = "op_signature",
srcs = [
"op_signature.cc",
],
hdrs = [
"op_signature.h",
],
compatible_with = get_compatible_with_portable(),
deps = [
"//tensorflow/compiler/mlir/lite/core/api:flatbuffer_conversions",
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/compiler/mlir/lite/schema:schema_utils",
"@flatbuffers//:runtime_cc",
],
)
tf_cc_test(
name = "op_signature_test",
srcs = [
"op_signature_test.cc",
],
data = [
"//tensorflow/compiler/mlir/lite:testdata/add.bin",
"//tensorflow/compiler/mlir/lite:testdata/multi_signatures.bin",
],
deps = [
":op_signature",
"//tensorflow/compiler/mlir/lite/core:absl_error_model_builder",
"//tensorflow/compiler/mlir/lite/core/c:tflite_common",
"//tensorflow/compiler/mlir/lite/schema:schema_fbs",
"//tensorflow/core/platform:resource_loader",
"@com_google_googletest//:gtest_main",
],
)
@@ -0,0 +1,272 @@
/* 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/compiler/mlir/lite/tools/versioning/op_signature.h"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "flatbuffers/vector.h" // from @flatbuffers
#include "tensorflow/compiler/mlir/lite/core/api/flatbuffer_conversions.h"
#include "tensorflow/compiler/mlir/lite/core/c/tflite_types.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_generated.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_utils.h"
namespace tflite {
namespace {
using tflite_file::flatbuffer_conversions::BuiltinDataAllocator;
using tflite_file::flatbuffer_conversions::ConvertTensorType;
using tflite_file::flatbuffer_conversions::ParseOpData;
// A BuiltinDataAllocator which just uses malloc()/free().
class MallocDataAllocator : public BuiltinDataAllocator {
public:
void* Allocate(size_t size, size_t alignment_hint) override {
return malloc(size);
}
void Deallocate(void* data) override { free(data); }
};
// Get the number of dimensions of a tensor with idx of an operator op.
inline int GetNumDims(const SubGraph* subgraph, const Operator* op, int idx) {
const flatbuffers::Vector<int32_t>* ret =
subgraph->tensors()->Get(op->inputs()->Get(idx))->shape();
if (ret) {
return ret->size();
} else {
return 0;
}
}
std::vector<OpSignatureTensorSpec> GetOpSignatureTensorSpecs(
const flatbuffers::Vector<int32_t>* tensors, const SubGraph* subgraph,
const Model* model) {
std::vector<OpSignatureTensorSpec> tensor_specs;
if (!tensors) {
return tensor_specs;
}
for (size_t i = 0; i < tensors->size(); ++i) {
int32_t tensor_no = tensors->Get(i);
OpSignatureTensorSpec tensor_spec = {kTfLiteNoType};
if (tensor_no >= 0) {
if (subgraph->tensors() &&
static_cast<size_t>(tensor_no) < subgraph->tensors()->size()) {
auto* fb_tensor = subgraph->tensors()->Get(tensor_no);
ConvertTensorType(fb_tensor->type(), &tensor_spec.type).IgnoreError();
auto buffer_idx = fb_tensor->buffer();
// Check if the tensor is a constant tensor.
if (buffer_idx != 0 && buffer_idx < model->buffers()->size()) {
auto* buffer = model->buffers()->Get(buffer_idx);
if (buffer->data() && !buffer->data()->empty()) {
tensor_spec.is_const = true;
}
}
const flatbuffers::Vector<int32_t>* shape_vec = fb_tensor->shape();
if (shape_vec) {
for (size_t j = 0; j < shape_vec->size(); ++j) {
tensor_spec.dims.push_back(shape_vec->Get(j));
}
}
const flatbuffers::Vector<int32_t>* shape_signature_vec =
fb_tensor->shape_signature();
tensor_spec.is_shape_dynamic = false;
if (shape_signature_vec) {
for (size_t j = 0; j < shape_signature_vec->size(); ++j) {
if (shape_signature_vec->Get(j) == -1) {
tensor_spec.is_shape_dynamic = true;
break;
}
}
}
}
}
tensor_specs.push_back(tensor_spec);
}
return tensor_specs;
}
bool IsTensorSizeEqual(size_t tensor_a_size, int tensor_b_size) {
return tensor_b_size >= 0 &&
static_cast<size_t>(tensor_b_size) == tensor_a_size;
}
} // namespace
OpSignature GetOpSignature(const OperatorCode* op_code, const Operator* op,
const SubGraph* subgraph, const Model* model) {
auto builtin_code = GetBuiltinCode(op_code);
OpSignature op_sig = {builtin_code};
std::memset(&op_sig.ext_options, 0, sizeof(op_sig.ext_options));
if (builtin_code != BuiltinOperator_CUSTOM) {
MallocDataAllocator allocator;
ParseOpData(op, builtin_code, &allocator, &op_sig.builtin_data)
.IgnoreError();
} else {
op_sig.custom_name = op_code->custom_code()->str();
}
switch (builtin_code) {
case BuiltinOperator_DEPTHWISE_CONV_2D: {
const Tensor* filter_tensor =
subgraph->tensors()->Get(op->inputs()->Get(1));
const QuantizationParameters* filter_quant =
filter_tensor->quantization();
int num_channels = filter_tensor->shape()->Get(3);
if (filter_quant && num_channels > 0 && filter_quant->scale() &&
filter_quant->scale()->size() == static_cast<size_t>(num_channels)) {
op_sig.ext_options.depthwise_conv_2d.is_per_channel_quantized = true;
}
} break;
case BuiltinOperator_FULLY_CONNECTED: {
const Tensor* weight_tensor =
subgraph->tensors()->Get(op->inputs()->Get(1));
op_sig.ext_options.fully_connected.sparse_weight =
(weight_tensor->sparsity() != nullptr);
const QuantizationParameters* weight_quant =
weight_tensor->quantization();
if (weight_quant && weight_quant->scale() &&
!weight_quant->scale()->empty() && weight_tensor->shape() &&
!weight_tensor->shape()->empty()) {
op_sig.ext_options.fully_connected.is_per_channel_quantized =
IsTensorSizeEqual(weight_quant->scale()->size(),
weight_tensor->shape()->Get(0));
}
} break;
case BuiltinOperator_MUL: {
if (op->inputs()->size() < 2 || op->outputs()->empty()) {
break;
}
const Tensor* input1_tensor =
subgraph->tensors()->Get(op->inputs()->Get(0));
const Tensor* input2_tensor =
subgraph->tensors()->Get(op->inputs()->Get(1));
const Tensor* output_tensor =
subgraph->tensors()->Get(op->outputs()->Get(0));
const QuantizationParameters* input1_quant =
input1_tensor->quantization();
const QuantizationParameters* input2_qunt = input2_tensor->quantization();
const QuantizationParameters* output_quant =
output_tensor->quantization();
if (input1_quant && input1_quant->scale() &&
!input1_quant->scale()->empty() && input2_qunt &&
input2_qunt->scale() && !input2_qunt->scale()->empty() &&
output_quant && output_quant->scale() &&
!output_quant->scale()->empty()) {
op_sig.ext_options.mul.input1_scale = input1_quant->scale()->Get(0);
op_sig.ext_options.mul.input2_scale = input2_qunt->scale()->Get(0);
op_sig.ext_options.mul.output_scale = output_quant->scale()->Get(0);
}
if (input1_quant || input2_qunt) {
op_sig.ext_options.mul.input_quantized = true;
}
} break;
case BuiltinOperator_CONV_2D: {
const Tensor* input_tensor =
subgraph->tensors()->Get(op->inputs()->Get(0));
const Tensor* filter_tensor =
subgraph->tensors()->Get(op->inputs()->Get(1));
const QuantizationParameters* filter_quant =
filter_tensor->quantization();
int num_filters = filter_tensor->shape()->Get(0);
if (filter_quant && num_filters > 0 && filter_quant->scale() &&
filter_quant->scale()->size() == static_cast<size_t>(num_filters)) {
op_sig.ext_options.conv_2d.is_per_channel_quantized = true;
}
if (input_tensor->shape() && !input_tensor->shape()->empty()) {
int num_input_channels = input_tensor->shape()->Get(3);
int num_filter_input_channels = filter_tensor->shape()->Get(3);
op_sig.ext_options.conv_2d.is_grouped_convolution =
num_input_channels != num_filter_input_channels;
} else {
op_sig.ext_options.conv_2d.is_grouped_convolution = false;
}
} break;
case BuiltinOperator_STRIDED_SLICE: {
op_sig.ext_options.strided_slice.num_dims = GetNumDims(subgraph, op, 0);
} break;
case BuiltinOperator_ABS: {
if (subgraph->tensors()->Get(op->inputs()->Get(0))->quantization()) {
op_sig.ext_options.abs.input_quantized = true;
}
} break;
case BuiltinOperator_DEQUANTIZE: {
const Tensor* input_tensor =
subgraph->tensors()->Get(op->inputs()->Get(0));
const QuantizationParameters* input_quant = input_tensor->quantization();
if (input_quant && input_quant->scale() &&
input_quant->scale()->size() > 1 &&
IsTensorSizeEqual(
input_quant->scale()->size(),
input_tensor->shape()->Get(input_quant->quantized_dimension()))) {
op_sig.ext_options.dequantize.is_per_channel_quantized = true;
}
} break;
case BuiltinOperator_QUANTIZE: {
const Tensor* output_tensor =
subgraph->tensors()->Get(op->outputs()->Get(0));
const QuantizationParameters* output_quant =
output_tensor->quantization();
if (output_quant && output_quant->scale() &&
output_quant->scale()->size() > 1 &&
IsTensorSizeEqual(output_quant->scale()->size(),
output_tensor->shape()->Get(
output_quant->quantized_dimension()))) {
op_sig.ext_options.quantize.is_per_channel_quantized = true;
}
} break;
case BuiltinOperator_ADD: {
if (subgraph->tensors()->Get(op->inputs()->Get(0))->quantization()) {
op_sig.ext_options.add.input_quantized = true;
}
} break;
case BuiltinOperator_EMBEDDING_LOOKUP: {
const Tensor* table_tensor =
subgraph->tensors()->Get(op->inputs()->Get(1));
const QuantizationParameters* table_quant = table_tensor->quantization();
if (table_quant && table_quant->scale() &&
!table_quant->scale()->empty() && table_tensor->shape() &&
!table_tensor->shape()->empty()) {
op_sig.ext_options.embedding_lookup.is_per_channel_quantized =
table_quant->scale()->size() > 1 &&
IsTensorSizeEqual(table_quant->scale()->size(),
table_tensor->shape()->Get(0));
}
} break;
default:
break;
}
op_sig.inputs = GetOpSignatureTensorSpecs(op->inputs(), subgraph, model);
op_sig.outputs = GetOpSignatureTensorSpecs(op->outputs(), subgraph, model);
op_sig.version = op_code->version();
return op_sig;
}
} // namespace tflite
@@ -0,0 +1,96 @@
/* 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_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_SIGNATURE_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_SIGNATURE_H_
#include <cstdint>
#include <string>
#include <vector>
#include "tensorflow/compiler/mlir/lite/core/c/tflite_types.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_generated.h"
namespace tflite {
// OpSignature contains operator parameters for version functions.
typedef struct {
TfLiteType type;
std::vector<int32_t> dims;
bool is_const;
bool is_shape_dynamic;
} OpSignatureTensorSpec;
typedef struct {
BuiltinOperator op;
std::vector<OpSignatureTensorSpec> inputs;
std::vector<OpSignatureTensorSpec> outputs;
void* builtin_data;
int version;
const void* custom_initial_data;
std::string custom_name;
union {
struct {
bool is_per_channel_quantized;
bool is_grouped_convolution;
} conv_2d;
struct {
bool is_per_channel_quantized;
} depthwise_conv_2d;
struct {
// TODO(b/156530611): Make this global when more ops support sparse
// computation.
bool sparse_weight;
bool is_per_channel_quantized;
} fully_connected;
struct {
float input1_scale;
float input2_scale;
float output_scale;
bool input_quantized;
} mul;
struct {
int32_t num_dims;
} strided_slice;
struct {
bool input_quantized;
} abs;
struct {
bool is_per_channel_quantized;
} dequantize;
struct {
bool is_per_channel_quantized;
} quantize;
struct {
bool input_quantized;
} add;
struct {
bool is_per_channel_quantized;
} embedding_lookup;
} ext_options;
} OpSignature;
// Generate OpSignature with the given OperatorCode, Operator and Tensors (from
// SubGraph). The OpSignature will be used by GetBuiltinOperatorVersion() and
// mostly input and output tensor types are enough to figure out op version.
// But some ops (DEPTHWISE_CONV_2D, FULLY_CONNECTED, ...) require to pass their
// options to decide op version.
//
// WARNING: The caller is responsible to free the allocated
// OpSignature.builtin_data memory.
OpSignature GetOpSignature(const OperatorCode* op_code, const Operator* op,
const SubGraph* subgraph, const Model* model);
} // namespace tflite
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_SIGNATURE_H_
@@ -0,0 +1,99 @@
/* 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/compiler/mlir/lite/tools/versioning/op_signature.h"
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "tensorflow/compiler/mlir/lite/core/absl_error_model_builder.h"
#include "tensorflow/compiler/mlir/lite/core/c/tflite_types.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_generated.h"
#include "tensorflow/core/platform/resource_loader.h"
namespace tflite {
TEST(GetOpSignature, FlatBufferModel) {
const std::string& full_path = tensorflow::GetDataDependencyFilepath(
"tensorflow/compiler/mlir/lite/testdata/add.bin");
auto fb_model =
mlir::TFL::FlatBufferModelAbslError::BuildFromFile(full_path.data());
ASSERT_TRUE(fb_model);
auto model = fb_model->GetModel();
auto subgraphs = model->subgraphs();
const SubGraph* subgraph = subgraphs->Get(0);
const Operator* op1 = subgraph->operators()->Get(0);
const OperatorCode* op_code1 =
model->operator_codes()->Get(op1->opcode_index());
OpSignature op_sig = GetOpSignature(op_code1, op1, subgraph, model);
EXPECT_EQ(op_sig.op, BuiltinOperator_ADD);
EXPECT_EQ(op_sig.inputs[0].type, kTfLiteFloat32);
EXPECT_EQ(op_sig.inputs[0].dims.size(), 4);
EXPECT_FALSE(op_sig.inputs[0].is_const);
EXPECT_FALSE(op_sig.inputs[0].is_shape_dynamic);
EXPECT_EQ(op_sig.outputs[0].type, kTfLiteFloat32);
EXPECT_FALSE(op_sig.outputs[0].is_const);
EXPECT_EQ(op_sig.outputs[0].dims.size(), 4);
EXPECT_FALSE(op_sig.outputs[0].is_shape_dynamic);
EXPECT_NE(op_sig.builtin_data, nullptr);
EXPECT_EQ(op_sig.version, 1);
free(op_sig.builtin_data);
const Operator* op2 = subgraph->operators()->Get(1);
const OperatorCode* op_code2 =
model->operator_codes()->Get(op2->opcode_index());
op_sig = GetOpSignature(op_code2, op2, subgraph, model);
EXPECT_EQ(op_sig.op, BuiltinOperator_ADD);
EXPECT_EQ(op_sig.inputs[0].type, kTfLiteFloat32);
EXPECT_EQ(op_sig.inputs[0].dims.size(), 4);
EXPECT_FALSE(op_sig.inputs[0].is_const);
EXPECT_FALSE(op_sig.inputs[0].is_shape_dynamic);
EXPECT_EQ(op_sig.outputs[0].type, kTfLiteFloat32);
EXPECT_FALSE(op_sig.outputs[0].is_const);
EXPECT_EQ(op_sig.outputs[0].dims.size(), 4);
EXPECT_FALSE(op_sig.outputs[0].is_shape_dynamic);
EXPECT_NE(op_sig.builtin_data, nullptr);
EXPECT_EQ(op_sig.version, 1);
free(op_sig.builtin_data);
const std::string& full_path3 = tensorflow::GetDataDependencyFilepath(
"tensorflow/compiler/mlir/lite/testdata/multi_signatures.bin");
auto fb_model3 =
mlir::TFL::FlatBufferModelAbslError::BuildFromFile(full_path3.data());
ASSERT_TRUE(fb_model3);
auto model3 = fb_model3->GetModel();
auto subgraphs3 = model3->subgraphs();
const SubGraph* subgraph3 = subgraphs3->Get(0);
const Operator* op3 = subgraph3->operators()->Get(0);
const OperatorCode* op_code3 =
model3->operator_codes()->Get(op3->opcode_index());
op_sig = GetOpSignature(op_code3, op3, subgraph3, model3);
EXPECT_EQ(op_sig.op, BuiltinOperator_ADD);
EXPECT_EQ(op_sig.inputs[0].type, kTfLiteFloat32);
EXPECT_EQ(op_sig.inputs[0].dims.size(), 1);
EXPECT_FALSE(op_sig.inputs[0].is_const);
EXPECT_TRUE(op_sig.inputs[0].is_shape_dynamic);
EXPECT_EQ(op_sig.outputs[0].type, kTfLiteFloat32);
EXPECT_FALSE(op_sig.outputs[0].is_const);
EXPECT_EQ(op_sig.outputs[0].dims.size(), 1);
EXPECT_TRUE(op_sig.outputs[0].is_shape_dynamic);
EXPECT_NE(op_sig.builtin_data, nullptr);
EXPECT_EQ(op_sig.version, 1);
free(op_sig.builtin_data);
}
} // namespace tflite
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,33 @@
/* 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_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_VERSION_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_VERSION_H_
#include <cstdint>
#include "tensorflow/compiler/mlir/lite/schema/mutable/schema_generated.h" // IWYU pragma: keep
#include "tensorflow/compiler/mlir/lite/tools/versioning/op_signature.h"
namespace tflite {
// Returns version of builtin ops by the given signature.
int GetBuiltinOperatorVersion(const OpSignature& op_sig);
// Update operator's version of the given TFL flatbuffer model.
void UpdateOpVersion(uint8_t* model_buffer_pointer);
} // namespace tflite
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_OP_VERSION_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,544 @@
/* 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/compiler/mlir/lite/tools/versioning/runtime_version.h"
#include <cstdint>
#include <cstring>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/log/log.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "tensorflow/compiler/mlir/lite/schema/mutable/schema_generated.h"
#include "tensorflow/compiler/mlir/lite/schema/schema_utils.h"
namespace tflite {
bool CompareRuntimeVersion(const std::string& v1, const std::string& v2) {
const std::vector<std::string> vec1 = absl::StrSplit(v1, '.');
const std::vector<std::string> vec2 = absl::StrSplit(v2, '.');
int i = 0;
while (i < vec1.size() && i < vec2.size()) {
int v1_val, v2_val;
if (absl::SimpleAtoi(vec1[i], &v1_val) &&
absl::SimpleAtoi(vec2[i], &v2_val)) {
if (v1_val != v2_val) return v1_val < v2_val;
}
++i;
}
// If there are remaining items in v2 not being compared, then v1 should
// precede v2.
return i < vec2.size();
}
std::string FindMinimumRuntimeVersionForOp(tflite::BuiltinOperator op_code,
int op_version) {
// A map from the version key of an op to its minimum runtime version.
// For example, {{kAveragePool, 1}, "1.5.0"}, means the 1st version of
// AveragePool requires a minimum TF Lite runtime version '1.5.0`.
// NOTE: When adding a new op version pair, associate it with the current
// runtime version defined in tensorflow/core/public/version.h.
static const std::map<std::pair<BuiltinOperator, int>, std::string>*
op_version_map =
new std::map<std::pair<BuiltinOperator, int>, std::string>({
{{BuiltinOperator_AVERAGE_POOL_2D, 1}, "1.5.0"},
{{BuiltinOperator_AVERAGE_POOL_2D, 2}, "1.14.0"},
{{BuiltinOperator_AVERAGE_POOL_2D, 3}, "2.3.0"},
{{BuiltinOperator_BATCH_MATMUL, 1}, "2.3.0"},
{{BuiltinOperator_BATCH_MATMUL, 2}, "2.3.0"},
{{BuiltinOperator_BATCH_MATMUL, 3}, "2.4.0"},
{{BuiltinOperator_BATCH_MATMUL, 4}, "2.5.0"},
// The version one of broadcast to op won't be not supported since
// the version one was rollbacked and the builtin op code number
// has been changed because of builtin op code shortage problem.
{{BuiltinOperator_BROADCAST_TO, 2}, "2.5.0"},
{{BuiltinOperator_BROADCAST_TO, 3}, "2.5.0"},
{{BuiltinOperator_CONV_2D, 1}, "1.5.0"},
{{BuiltinOperator_CONV_2D, 2}, "1.14.0"},
{{BuiltinOperator_CONV_2D, 3}, "1.14.0"},
{{BuiltinOperator_CONV_2D, 4}, "2.3.0"},
{{BuiltinOperator_CONV_2D, 5}, "2.4.0"},
{{BuiltinOperator_CONV_2D, 6}, "2.9.0"},
{{BuiltinOperator_CONV_2D, 7}, "2.11.0"},
{{BuiltinOperator_CONV_2D, 8}, "2.15.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 1}, "1.5.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 2}, "1.12.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 3}, "1.14.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 4}, "2.2.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 5}, "2.3.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 6}, "2.3.0"},
{{BuiltinOperator_DEPTHWISE_CONV_2D, 7}, "2.11.0"},
{{BuiltinOperator_ADD, 1}, "1.5.0"},
{{BuiltinOperator_ADD, 2}, "1.14.0"},
{{BuiltinOperator_ADD, 3}, "2.4.0"},
{{BuiltinOperator_ADD, 4}, "2.6.0"},
{{BuiltinOperator_ADD, 5}, "2.13.0"},
{{BuiltinOperator_ADD, 6}, "2.23.0"},
{{BuiltinOperator_ADD_N, 1}, "1.14.0"},
{{BuiltinOperator_SPACE_TO_BATCH_ND, 1}, "1.6.0"},
{{BuiltinOperator_SPACE_TO_BATCH_ND, 2}, "1.14.0"},
{{BuiltinOperator_SPACE_TO_BATCH_ND, 3}, "2.3.0"},
{{BuiltinOperator_SPACE_TO_BATCH_ND, 4}, "2.12.0"},
{{BuiltinOperator_SUB, 1}, "1.6.0"},
{{BuiltinOperator_SUB, 2}, "1.14.0"},
{{BuiltinOperator_SUB, 3}, "2.3.0"},
{{BuiltinOperator_SUB, 4}, "2.4.0"},
{{BuiltinOperator_SUB, 5}, "2.4.0"},
{{BuiltinOperator_DENSIFY, 1}, "2.2.0"},
{{BuiltinOperator_DIV, 1}, "1.6.0"},
{{BuiltinOperator_DIV, 2}, "2.3.0"},
{{BuiltinOperator_BATCH_TO_SPACE_ND, 1}, "1.6.0"},
{{BuiltinOperator_BATCH_TO_SPACE_ND, 2}, "1.14.0"},
{{BuiltinOperator_BATCH_TO_SPACE_ND, 3}, "2.3.0"},
{{BuiltinOperator_BATCH_TO_SPACE_ND, 4}, "2.12.0"},
{{BuiltinOperator_CAST, 1}, "1.5.0"},
{{BuiltinOperator_CAST, 2}, "2.7.0"},
{{BuiltinOperator_CAST, 3}, "2.8.0"},
{{BuiltinOperator_CAST, 4}, "2.9.0"},
{{BuiltinOperator_CAST, 5}, "2.12.0"},
{{BuiltinOperator_CAST, 6}, "2.15.0"},
{{BuiltinOperator_CAST, 7}, "2.17.0"},
{{BuiltinOperator_CAST, 8}, "2.21.0"},
{{BuiltinOperator_CONCATENATION, 1}, "1.5.0"},
{{BuiltinOperator_CONCATENATION, 2}, "1.14.0"},
{{BuiltinOperator_CONCATENATION, 3}, "2.3.0"},
{{BuiltinOperator_CONCATENATION, 4}, "2.14.0"},
{{BuiltinOperator_CONCATENATION, 5}, "2.21.0"},
{{BuiltinOperator_CONCATENATION, 6}, "2.23.0"},
{{BuiltinOperator_DEPTH_TO_SPACE, 1}, "2.1.0"},
{{BuiltinOperator_DEPTH_TO_SPACE, 2}, "2.5.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP, 1}, "1.13.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP, 2}, "1.14.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP, 3}, "1.14.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP, 4}, "2.18.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP, 5}, "2.21.0"},
{{BuiltinOperator_EMBEDDING_LOOKUP_SPARSE, 1}, "1.5.0"},
{{BuiltinOperator_FAKE_QUANT, 1}, "1.5.0"},
{{BuiltinOperator_FAKE_QUANT, 2}, "1.10.0"},
{{BuiltinOperator_FULLY_CONNECTED, 1}, "1.5.0"},
{{BuiltinOperator_FULLY_CONNECTED, 2}, "1.10.0"},
{{BuiltinOperator_FULLY_CONNECTED, 3}, "1.14.0"},
{{BuiltinOperator_FULLY_CONNECTED, 4}, "1.14.0"},
{{BuiltinOperator_FULLY_CONNECTED, 5}, "2.0.0"},
{{BuiltinOperator_FULLY_CONNECTED, 6}, "2.1.0"},
{{BuiltinOperator_FULLY_CONNECTED, 7}, "2.3.0"},
{{BuiltinOperator_FULLY_CONNECTED, 8}, "2.3.0"},
{{BuiltinOperator_FULLY_CONNECTED, 9}, "2.3.0"},
{{BuiltinOperator_FULLY_CONNECTED, 10}, "2.11.0"},
{{BuiltinOperator_FULLY_CONNECTED, 11}, "2.15.0"},
{{BuiltinOperator_FULLY_CONNECTED, 12}, "2.17.0"},
{{BuiltinOperator_FULLY_CONNECTED, 13}, "2.18.0"},
{{BuiltinOperator_FULLY_CONNECTED, 14}, "2.21.0"},
{{BuiltinOperator_GATHER, 1}, "1.6.0"},
{{BuiltinOperator_GATHER, 2}, "1.14.0"},
{{BuiltinOperator_GATHER, 3}, "1.15.0"},
{{BuiltinOperator_GATHER, 4}, "2.4.0"},
{{BuiltinOperator_GATHER, 5}, "2.5.0"},
{{BuiltinOperator_GATHER, 6}, "2.13.0"},
{{BuiltinOperator_GATHER, 7}, "2.15.0"},
{{BuiltinOperator_GATHER_ND, 1}, "1.14.0"},
{{BuiltinOperator_GATHER_ND, 2}, "2.3.0"},
{{BuiltinOperator_GATHER_ND, 3}, "2.5.0"},
{{BuiltinOperator_GATHER_ND, 4}, "2.13.0"},
{{BuiltinOperator_GATHER_ND, 5}, "2.16.0"},
{{BuiltinOperator_HASHTABLE_LOOKUP, 1}, "1.5.0"},
{{BuiltinOperator_SVDF, 1}, "1.5.0"},
{{BuiltinOperator_SVDF, 2}, "1.14.0"},
{{BuiltinOperator_SVDF, 3}, "2.2.0"},
{{BuiltinOperator_SVDF, 4}, "2.3.0"},
{{BuiltinOperator_L2_NORMALIZATION, 1}, "1.5.0"},
{{BuiltinOperator_L2_NORMALIZATION, 2}, "1.14.0"},
{{BuiltinOperator_L2_POOL_2D, 1}, "1.5.0"},
{{BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION, 1}, "1.5.0"},
{{BuiltinOperator_MAX_POOL_2D, 1}, "1.5.0"},
{{BuiltinOperator_MAX_POOL_2D, 2}, "1.14.0"},
{{BuiltinOperator_MAX_POOL_2D, 3}, "2.3.0"},
{{BuiltinOperator_MAXIMUM, 1}, "1.14.0"},
{{BuiltinOperator_MAXIMUM, 2}, "1.14.0"},
{{BuiltinOperator_MAXIMUM, 3}, "2.3.0"},
{{BuiltinOperator_MAXIMUM, 4}, "2.3.0"},
{{BuiltinOperator_MINIMUM, 1}, "1.14.0"},
{{BuiltinOperator_MINIMUM, 2}, "1.14.0"},
{{BuiltinOperator_MINIMUM, 3}, "2.3.0"},
{{BuiltinOperator_MINIMUM, 4}, "2.3.0"},
{{BuiltinOperator_MUL, 1}, "1.5.0"},
{{BuiltinOperator_MUL, 2}, "1.14.0"},
{{BuiltinOperator_MUL, 3}, "1.15.0"},
{{BuiltinOperator_MUL, 4}, "2.3.0"},
{{BuiltinOperator_MUL, 5}, "2.6.0"},
{{BuiltinOperator_MUL, 6}, "2.11.0"},
{{BuiltinOperator_MUL, 7}, "2.13.0"},
{{BuiltinOperator_MUL, 8}, "2.23.0"},
{{BuiltinOperator_NON_MAX_SUPPRESSION_V4, 1}, "2.1.0"},
{{BuiltinOperator_NON_MAX_SUPPRESSION_V5, 1}, "2.1.0"},
{{BuiltinOperator_PAD, 1}, "1.5.0"},
{{BuiltinOperator_PAD, 2}, "1.14.0"},
{{BuiltinOperator_PAD, 3}, "2.4.0"},
{{BuiltinOperator_PAD, 4}, "2.6.0"},
{{BuiltinOperator_PAD, 5}, "2.20.0"},
{{BuiltinOperator_TILE, 1}, "1.10.1"},
{{BuiltinOperator_TILE, 2}, "2.2.0"},
{{BuiltinOperator_TILE, 3}, "2.8.0"},
{{BuiltinOperator_PADV2, 1}, "1.9.0"},
{{BuiltinOperator_PADV2, 2}, "1.14.0"},
{{BuiltinOperator_PADV2, 3}, "2.4.0"},
{{BuiltinOperator_PADV2, 4}, "2.6.0"},
{{BuiltinOperator_PADV2, 5}, "2.20.0"},
{{BuiltinOperator_RESHAPE, 1}, "1.5.0"},
{{BuiltinOperator_SOFTMAX, 1}, "1.5.0"},
{{BuiltinOperator_SOFTMAX, 2}, "1.14.0"},
{{BuiltinOperator_SOFTMAX, 3}, "2.3.0"},
{{BuiltinOperator_SOFTMAX, 4}, "2.23.0"},
{{BuiltinOperator_SPACE_TO_DEPTH, 1}, "1.5.0"},
{{BuiltinOperator_SPACE_TO_DEPTH, 2}, "1.14.0"},
{{BuiltinOperator_TRANSPOSE, 1}, "1.6.0"},
{{BuiltinOperator_TRANSPOSE, 2}, "1.14.0"},
{{BuiltinOperator_TRANSPOSE, 3}, "1.15.0"},
{{BuiltinOperator_TRANSPOSE, 4}, "2.3.0"},
{{BuiltinOperator_TRANSPOSE, 5}, "2.4.0"},
{{BuiltinOperator_TRANSPOSE, 6}, "2.12.0"},
{{BuiltinOperator_TRANSPOSE, 7}, "2.14.4"},
{{BuiltinOperator_TRANSPOSE, 8}, "2.22.0"},
{{BuiltinOperator_TRANSPOSE, 9}, "2.23.0"},
{{BuiltinOperator_LSTM, 1}, "1.7.0"},
{{BuiltinOperator_LSTM, 2}, "1.10.0"},
{{BuiltinOperator_LSTM, 3}, "1.14.0"},
{{BuiltinOperator_LSTM, 4}, "2.3.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 1}, "1.13.1"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 2}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 3}, "2.3.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM, 4}, "2.12.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 1}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 2}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM, 3}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 1}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 2}, "1.14.0"},
{{BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN, 3}, "2.3.0"},
{{BuiltinOperator_MEAN, 1}, "1.6.0"},
{{BuiltinOperator_MEAN, 2}, "1.14.0"},
{{BuiltinOperator_MEAN, 3}, "2.4.0"},
{{BuiltinOperator_SUM, 1}, "1.10.0"},
{{BuiltinOperator_SUM, 2}, "1.15.0"},
{{BuiltinOperator_REDUCE_MAX, 1}, "1.11.0"},
{{BuiltinOperator_REDUCE_MAX, 2}, "1.14.0"},
{{BuiltinOperator_REDUCE_MAX, 3}, "2.5.0"},
{{BuiltinOperator_REDUCE_MIN, 1}, "1.11.0"},
{{BuiltinOperator_REDUCE_MIN, 2}, "1.14.0"},
{{BuiltinOperator_REDUCE_MIN, 3}, "2.5.0"},
{{BuiltinOperator_REDUCE_PROD, 1}, "1.11.0"},
{{BuiltinOperator_REDUCE_PROD, 2}, "2.6.0"},
{{BuiltinOperator_REDUCE_ANY, 1}, "1.11.0"},
{{BuiltinOperator_RELU6, 1}, "1.5.0"},
{{BuiltinOperator_RELU6, 2}, "1.14.0"},
{{BuiltinOperator_RELU6, 3}, "2.5.0"},
{{BuiltinOperator_RESIZE_BILINEAR, 1}, "1.7.0"},
{{BuiltinOperator_RESIZE_BILINEAR, 2}, "1.14.0"},
{{BuiltinOperator_RESIZE_BILINEAR, 3}, "2.2.0"},
{{BuiltinOperator_RESIZE_BILINEAR, 4}, "2.5.0"},
{{BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, 1}, "1.13.1"},
{{BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, 2}, "1.14.0"},
{{BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, 3}, "2.3.0"},
{{BuiltinOperator_RESIZE_NEAREST_NEIGHBOR, 4}, "2.4.0"},
{{BuiltinOperator_RNN, 1}, "1.5.0"},
{{BuiltinOperator_RNN, 2}, "1.14.0"},
{{BuiltinOperator_RNN, 3}, "2.3.0"},
{{BuiltinOperator_SKIP_GRAM, 1}, "1.5.0"},
{{BuiltinOperator_SQUEEZE, 1}, "1.6.0"},
{{BuiltinOperator_SQUEEZE, 2}, "2.5.0"},
{{BuiltinOperator_SPLIT, 1}, "1.5.0"},
{{BuiltinOperator_SPLIT, 2}, "1.14.0"},
{{BuiltinOperator_SPLIT, 3}, "1.14.0"},
{{BuiltinOperator_SPLIT, 4}, "2.3.0"},
{{BuiltinOperator_SPLIT_V, 1}, "1.13.1"},
{{BuiltinOperator_SPLIT_V, 2}, "2.3.0"},
{{BuiltinOperator_STRIDED_SLICE, 1}, "1.6.0"},
{{BuiltinOperator_STRIDED_SLICE, 2}, "1.14.0"},
{{BuiltinOperator_STRIDED_SLICE, 3}, "2.1.0"},
{{BuiltinOperator_STRIDED_SLICE, 4}, "2.2.0"},
{{BuiltinOperator_STRIDED_SLICE, 5}, "2.5.0"},
{{BuiltinOperator_STRIDED_SLICE, 6}, "2.6.0"},
{{BuiltinOperator_STRIDED_SLICE, 7}, "2.14.0"},
{{BuiltinOperator_STRIDED_SLICE, 8}, "2.14.0"},
{{BuiltinOperator_TOPK_V2, 1}, "1.7.0"},
{{BuiltinOperator_TOPK_V2, 2}, "1.14.0"},
{{BuiltinOperator_TOPK_V2, 3}, "2.13.0"},
{{BuiltinOperator_ARG_MAX, 1}, "1.9.0"},
{{BuiltinOperator_ARG_MAX, 2}, "1.14.0"},
{{BuiltinOperator_ARG_MAX, 3}, "2.9.0"},
{{BuiltinOperator_ARG_MIN, 1}, "1.9.0"},
{{BuiltinOperator_ARG_MIN, 2}, "1.14.0"},
{{BuiltinOperator_ARG_MIN, 3}, "2.9.0"},
{{BuiltinOperator_TRANSPOSE_CONV, 1}, "1.9.0"},
{{BuiltinOperator_TRANSPOSE_CONV, 2}, "2.2.0"},
{{BuiltinOperator_TRANSPOSE_CONV, 3}, "2.3.0"},
{{BuiltinOperator_TRANSPOSE_CONV, 4}, "2.13.0"},
{{BuiltinOperator_TRANSPOSE_CONV, 5}, "2.15.0"},
{{BuiltinOperator_SPARSE_TO_DENSE, 1}, "1.9.0"},
{{BuiltinOperator_SPARSE_TO_DENSE, 2}, "1.14.0"},
{{BuiltinOperator_SPARSE_TO_DENSE, 3}, "1.15.0"},
{{BuiltinOperator_EXPAND_DIMS, 1}, "1.10.0"},
{{BuiltinOperator_PACK, 1}, "1.11.0"},
{{BuiltinOperator_PACK, 2}, "1.14.0"},
{{BuiltinOperator_PACK, 3}, "2.3.0"},
{{BuiltinOperator_PACK, 4}, "2.13.0"},
{{BuiltinOperator_SHAPE, 1}, "1.10.0"},
{{BuiltinOperator_SLICE, 1}, "1.14.0"},
{{BuiltinOperator_SLICE, 2}, "1.14.0"},
{{BuiltinOperator_SLICE, 3}, "1.14.0"},
{{BuiltinOperator_SLICE, 4}, "2.4.0"},
{{BuiltinOperator_SLICE, 5}, "2.5.0"},
{{BuiltinOperator_SLICE, 6}, "2.14.0"},
{{BuiltinOperator_SLICE, 7}, "2.21.0"},
{{BuiltinOperator_SLICE, 8}, "2.23.0"},
{{BuiltinOperator_TANH, 1}, "1.14.0"},
{{BuiltinOperator_TANH, 2}, "1.14.0"},
{{BuiltinOperator_TANH, 3}, "2.3.0"},
{{BuiltinOperator_ONE_HOT, 1}, "1.11.0"},
{{BuiltinOperator_UNPACK, 1}, "1.11.0"},
{{BuiltinOperator_UNPACK, 2}, "1.14.0"},
{{BuiltinOperator_UNPACK, 3}, "2.2.0"},
{{BuiltinOperator_UNPACK, 4}, "2.3.0"},
{{BuiltinOperator_UNPACK, 5}, "2.22.0"},
{{BuiltinOperator_LEAKY_RELU, 1}, "1.13.1"},
{{BuiltinOperator_LEAKY_RELU, 2}, "2.3.0"},
{{BuiltinOperator_LOGISTIC, 1}, "1.14.0"},
{{BuiltinOperator_LOGISTIC, 2}, "1.14.0"},
{{BuiltinOperator_LOGISTIC, 3}, "2.3.0"},
{{BuiltinOperator_LOG_SOFTMAX, 1}, "1.14.0"},
{{BuiltinOperator_LOG_SOFTMAX, 2}, "1.14.0"},
{{BuiltinOperator_LSH_PROJECTION, 1}, "1.5.0"},
{{BuiltinOperator_SQUARED_DIFFERENCE, 1}, "1.13.1"},
{{BuiltinOperator_SQUARED_DIFFERENCE, 2}, "2.5.0"},
{{BuiltinOperator_MIRROR_PAD, 1}, "1.13.1"},
{{BuiltinOperator_MIRROR_PAD, 2}, "2.3.0"},
{{BuiltinOperator_MIRROR_PAD, 3}, "2.12.0"},
{{BuiltinOperator_UNIQUE, 1}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 1}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 2}, "1.14.0"},
{{BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN, 3}, "2.3.0"},
{{BuiltinOperator_WHERE, 1}, "1.14.0"},
{{BuiltinOperator_DEQUANTIZE, 1}, "1.13.1"},
{{BuiltinOperator_DEQUANTIZE, 2}, "1.14.0"},
{{BuiltinOperator_DEQUANTIZE, 3}, "1.15.0"},
{{BuiltinOperator_DEQUANTIZE, 4}, "2.2.0"},
{{BuiltinOperator_DEQUANTIZE, 5}, "2.7.0"},
{{BuiltinOperator_DEQUANTIZE, 6}, "2.18.0"},
{{BuiltinOperator_DEQUANTIZE, 7}, "2.21.0"},
{{BuiltinOperator_DEQUANTIZE, 8}, "2.22.0"},
{{BuiltinOperator_REVERSE_SEQUENCE, 1}, "1.14.0"},
{{BuiltinOperator_EQUAL, 1}, "1.14.0"},
{{BuiltinOperator_EQUAL, 2}, "1.14.0"},
{{BuiltinOperator_EQUAL, 3}, "2.3.0"},
{{BuiltinOperator_EQUAL, 4}, "2.13.0"},
{{BuiltinOperator_EQUAL, 5}, "2.21.0"},
{{BuiltinOperator_NOT_EQUAL, 1}, "1.14.0"},
{{BuiltinOperator_NOT_EQUAL, 2}, "1.14.0"},
{{BuiltinOperator_NOT_EQUAL, 3}, "2.3.0"},
{{BuiltinOperator_NOT_EQUAL, 4}, "2.21.0"},
{{BuiltinOperator_GREATER, 1}, "1.14.0"},
{{BuiltinOperator_GREATER, 2}, "1.14.0"},
{{BuiltinOperator_GREATER_EQUAL, 1}, "1.14.0"},
{{BuiltinOperator_GREATER_EQUAL, 2}, "1.14.0"},
{{BuiltinOperator_GREATER_EQUAL, 3}, "2.13.0"},
{{BuiltinOperator_LESS, 1}, "1.14.0"},
{{BuiltinOperator_LESS, 2}, "1.14.0"},
{{BuiltinOperator_LESS, 3}, "2.13.0"},
{{BuiltinOperator_LESS_EQUAL, 1}, "1.14.0"},
{{BuiltinOperator_LESS_EQUAL, 2}, "1.14.0"},
{{BuiltinOperator_SCATTER_ND, 1}, "2.1.0"},
{{BuiltinOperator_SEGMENT_SUM, 1}, "2.2.0"},
{{BuiltinOperator_SELECT, 1}, "1.14.0"},
{{BuiltinOperator_SELECT, 2}, "1.14.0"},
{{BuiltinOperator_SELECT, 3}, "2.12.0"},
{{BuiltinOperator_SELECT, 4}, "2.12.0"},
{{BuiltinOperator_SELECT_V2, 1}, "2.2.0"},
{{BuiltinOperator_SELECT_V2, 2}, "2.12.0"},
{{BuiltinOperator_IF, 1}, "1.15.0"},
{{BuiltinOperator_FLOOR_DIV, 1}, "1.14.0"},
{{BuiltinOperator_FLOOR_DIV, 2}, "1.14.0"},
{{BuiltinOperator_FLOOR_DIV, 3}, "2.13.0"},
{{BuiltinOperator_FLOOR, 1}, "1.9.0"},
{{BuiltinOperator_CEIL, 1}, "1.14.0"},
{{BuiltinOperator_MATRIX_DIAG, 1}, "1.14.0"},
{{BuiltinOperator_MATRIX_SET_DIAG, 1}, "1.14.0"},
{{BuiltinOperator_ELU, 1}, "1.14.0"},
{{BuiltinOperator_QUANTIZE, 1}, "1.14.0"},
{{BuiltinOperator_QUANTIZE, 2}, "1.15.0"},
{{BuiltinOperator_QUANTIZE, 3}, "2.7.0"},
{{BuiltinOperator_QUANTIZE, 4}, "2.21.0"},
{{BuiltinOperator_QUANTIZE, 5}, "2.21.0"},
{{BuiltinOperator_QUANTIZE, 6}, "2.21.0"},
{{BuiltinOperator_ROUND, 1}, "1.14.0"},
{{BuiltinOperator_RELU, 1}, "1.5.0"},
{{BuiltinOperator_RELU, 2}, "2.1.0"},
{{BuiltinOperator_RELU, 3}, "2.5.0"},
{{BuiltinOperator_RELU_N1_TO_1, 1}, "1.5.0"},
{{BuiltinOperator_RELU_0_TO_1, 1}, "2.10.0"},
{{BuiltinOperator_PRELU, 1}, "1.8.0"},
{{BuiltinOperator_EXP, 1}, "1.7.0"},
{{BuiltinOperator_EXP, 2}, "2.12.0"},
{{BuiltinOperator_COS, 1}, "1.14.0"},
{{BuiltinOperator_COS, 2}, "2.23.0"},
{{BuiltinOperator_NEG, 1}, "1.9.0"},
{{BuiltinOperator_POW, 1}, "1.10.0"},
{{BuiltinOperator_LOGICAL_OR, 1}, "1.11.0"},
{{BuiltinOperator_LOGICAL_AND, 1}, "1.11.0"},
{{BuiltinOperator_LOGICAL_NOT, 1}, "1.11.0"},
{{BuiltinOperator_FLOOR_MOD, 1}, "1.13.0"},
{{BuiltinOperator_FLOOR_MOD, 2}, "2.13.0"},
{{BuiltinOperator_RANGE, 1}, "1.13.0"},
{{BuiltinOperator_RANGE, 2}, "2.14.0"},
{{BuiltinOperator_SIN, 1}, "1.9.0"},
{{BuiltinOperator_SIN, 2}, "2.23.0"},
{{BuiltinOperator_LOG, 1}, "1.14.0"},
{{BuiltinOperator_LOG, 2}, "2.15.0"},
{{BuiltinOperator_SQRT, 1}, "1.10.0"},
{{BuiltinOperator_SQRT, 2}, "2.21.0"},
{{BuiltinOperator_RSQRT, 1}, "1.10.0"},
{{BuiltinOperator_RSQRT, 2}, "2.5.0"},
{{BuiltinOperator_RSQRT, 3}, "2.15.0"},
{{BuiltinOperator_SQUARE, 1}, "1.12.0"},
{{BuiltinOperator_ZEROS_LIKE, 1}, "1.12.0"},
{{BuiltinOperator_ABS, 1}, "1.13.0"},
{{BuiltinOperator_ABS, 2}, "2.4.0"},
{{BuiltinOperator_ABS, 3}, "2.5.0"},
{{BuiltinOperator_ABS, 4}, "2.6.0"},
{{BuiltinOperator_ABS, 5}, "2.12.0"},
{{BuiltinOperator_HARD_SWISH, 1}, "1.15.0"},
{{BuiltinOperator_FILL, 1}, "1.13.0"},
{{BuiltinOperator_FILL, 2}, "2.3.0"},
{{BuiltinOperator_FILL, 3}, "2.5.0"},
{{BuiltinOperator_FILL, 4}, "2.12.0"},
{{BuiltinOperator_REVERSE_V2, 1}, "1.14.0"},
{{BuiltinOperator_REVERSE_V2, 2}, "2.2.0"},
{{BuiltinOperator_REVERSE_V2, 3}, "2.5.0"},
{{BuiltinOperator_RANK, 1}, "1.14.0"},
{{BuiltinOperator_WHILE, 1}, "1.15.0"},
{{BuiltinOperator_CUMSUM, 1}, "2.4.0"},
{{BuiltinOperator_CALL_ONCE, 1}, "2.5.0"},
{{BuiltinOperator_RFFT2D, 1}, "2.5.0"},
{{BuiltinOperator_CONV_3D, 1}, "2.5.0"},
{{BuiltinOperator_IMAG, 1}, "2.5.0"},
{{BuiltinOperator_REAL, 1}, "2.5.0"},
{{BuiltinOperator_COMPLEX_ABS, 1}, "2.5.0"},
{{BuiltinOperator_HASHTABLE, 1}, "2.5.0"},
{{BuiltinOperator_HASHTABLE_FIND, 1}, "2.5.0"},
{{BuiltinOperator_HASHTABLE_IMPORT, 1}, "2.5.0"},
{{BuiltinOperator_HASHTABLE_SIZE, 1}, "2.5.0"},
{{BuiltinOperator_REDUCE_ALL, 1}, "2.6.0"},
{{BuiltinOperator_CONV_3D_TRANSPOSE, 1}, "2.6.0"},
{{BuiltinOperator_VAR_HANDLE, 1}, "2.6.0"},
{{BuiltinOperator_READ_VARIABLE, 1}, "2.6.0"},
{{BuiltinOperator_ASSIGN_VARIABLE, 1}, "2.6.0"},
{{BuiltinOperator_BROADCAST_ARGS, 1}, "2.6.0"},
{{BuiltinOperator_RANDOM_STANDARD_NORMAL, 1}, "2.8.0"},
{{BuiltinOperator_BUCKETIZE, 1}, "2.8.0"},
{{BuiltinOperator_WHERE, 2}, "2.8.0"},
{{BuiltinOperator_RANDOM_UNIFORM, 1}, "2.8.0"},
{{BuiltinOperator_MULTINOMIAL, 1}, "2.8.0"},
{{BuiltinOperator_GELU, 1}, "2.9.0"},
{{BuiltinOperator_GELU, 2}, "2.9.0"},
{{BuiltinOperator_GELU, 3}, "2.23.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 1}, "2.9.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 2}, "2.17.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 3}, "2.19.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 4}, "2.20.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 5}, "2.21.0"},
{{BuiltinOperator_DYNAMIC_UPDATE_SLICE, 6}, "2.22.0"},
{{BuiltinOperator_UNSORTED_SEGMENT_PROD, 1}, "2.10.0"},
{{BuiltinOperator_UNSORTED_SEGMENT_MAX, 1}, "2.10.0"},
{{BuiltinOperator_UNSORTED_SEGMENT_MIN, 1}, "2.11.0"},
{{BuiltinOperator_UNSORTED_SEGMENT_SUM, 1}, "2.10.0"},
{{BuiltinOperator_ATAN2, 1}, "2.10.0"},
{{BuiltinOperator_SIGN, 1}, "2.11.0"},
{{BuiltinOperator_SIGN, 2}, "2.12.0"},
{{BuiltinOperator_BITCAST, 1}, "2.13.0"},
{{BuiltinOperator_BITWISE_XOR, 1}, "2.13.0"},
{{BuiltinOperator_RIGHT_SHIFT, 1}, "2.13.0"},
{{BuiltinOperator_STABLEHLO_SCATTER, 1}, "2.15.0"},
{{BuiltinOperator_DILATE, 1}, "2.15.0"},
{{BuiltinOperator_STABLEHLO_RNG_BIT_GENERATOR, 1}, "2.15.0"},
{{BuiltinOperator_REDUCE_WINDOW, 1}, "2.15.0"},
{{BuiltinOperator_STABLEHLO_GATHER, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_ADD, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_MULTIPLY, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_REDUCE_WINDOW, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_MAXIMUM, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_MINIMUM, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_PAD, 1}, "2.16.0"},
{{BuiltinOperator_STABLEHLO_COMPOSITE, 1}, "2.17.0"},
{{BuiltinOperator_STABLEHLO_AND, 1}, "2.17.0"},
{{BuiltinOperator_STABLEHLO_SHIFT_LEFT, 1}, "2.17.0"},
{{BuiltinOperator_STABLEHLO_CBRT, 1}, "2.17.0"},
{{BuiltinOperator_STABLEHLO_CASE, 1}, "2.17.0"},
});
std::pair<BuiltinOperator, int> version_key = {op_code, op_version};
auto it = op_version_map->find(version_key);
if (it == op_version_map->end()) {
return std::string();
}
return it->second;
}
void UpdateMinimumRuntimeVersionForModel(uint8_t* model_buffer_pointer) {
auto model = GetMutableModel(model_buffer_pointer);
std::string model_min_version;
auto subgraphs = model->subgraphs();
for (int i = 0; i < subgraphs->Length(); ++i) {
const SubGraph* subgraph = subgraphs->Get(i);
for (int j = 0; j < subgraph->operators()->Length(); ++j) {
const Operator* op = subgraph->operators()->Get(j);
const OperatorCode* op_code =
model->operator_codes()->Get(op->opcode_index());
std::string runtime_version = FindMinimumRuntimeVersionForOp(
GetBuiltinCode(op_code), op_code->version());
// If we didn't find the current op version in the map, skip comparison.
if (runtime_version.empty()) {
continue;
}
if (CompareRuntimeVersion(model_min_version, runtime_version)) {
// Current min model runtime version should be bumped if we see a
// higher op version.
model_min_version = runtime_version;
}
}
}
// The size of the `min_runtime_version` metadata buffer is 16 bytes. If the
// generated `model_min_version` is equal or longer than 16 bytes, print a
// warning message and return.
if (model_min_version.size() >= 16) {
LOG(WARNING) << "Skip writing minimum runtime version string since it's "
<< "longer than 16 bytes.";
return;
}
// Copy over the bytes from `model_min_version` into the buffer.
for (int i = 0; i < model->metadata()->size(); ++i) {
if (model->metadata()->Get(i)->name()->str() == "min_runtime_version") {
auto buffer = model->metadata()->Get(i)->buffer();
auto buffer_data =
model->mutable_buffers()->GetMutableObject(buffer)->mutable_data();
memset(buffer_data->data(), 0, buffer_data->size());
memcpy(buffer_data->data(), model_min_version.data(),
model_min_version.size());
break;
}
}
}
} // namespace tflite
@@ -0,0 +1,40 @@
/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#ifndef TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_RUNTIME_VERSION_H_
#define TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_RUNTIME_VERSION_H_
#include <cstdint>
#include <string>
#include "flatbuffers/flatbuffers.h" // from @flatbuffers // IWYU pragma: keep
#include "tensorflow/compiler/mlir/lite/schema/mutable/schema_generated.h"
namespace tflite {
// Update minimum runtime version of the given TFL flatbuffer model.
void UpdateMinimumRuntimeVersionForModel(uint8_t* model_buffer_pointer);
// Find the minimum runtime version of a given op version. Return an empty
// string the version is not registered.
std::string FindMinimumRuntimeVersionForOp(tflite::BuiltinOperator op_code,
int op_version);
// Returns true if the first version string precedes the second.
// For example, '1.9' should precede '1.14', also '1.14' should precede
// '1.14.1'. If two version string is equal, then false will be returned.
bool CompareRuntimeVersion(const std::string&, const std::string&);
} // namespace tflite
#endif // TENSORFLOW_COMPILER_MLIR_LITE_TOOLS_VERSIONING_RUNTIME_VERSION_H_
@@ -0,0 +1,35 @@
/* Copyright 2025 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/compiler/mlir/lite/tools/versioning/runtime_version.h"
#include <string>
#include <gtest/gtest.h>
namespace tflite {
TEST(OpVersionTest, CompareRuntimeVersion) {
EXPECT_TRUE(CompareRuntimeVersion("1.9", "1.13"));
EXPECT_FALSE(CompareRuntimeVersion("1.13", "1.13"));
EXPECT_TRUE(CompareRuntimeVersion("1.14", "1.14.1"));
EXPECT_FALSE(CompareRuntimeVersion("1.14.1", "1.14"));
EXPECT_FALSE(CompareRuntimeVersion("1.14.1", "1.9"));
EXPECT_FALSE(CompareRuntimeVersion("1.0.9", "1.0.8"));
EXPECT_FALSE(CompareRuntimeVersion("2.1.0", "1.2.0"));
EXPECT_TRUE(CompareRuntimeVersion("", "1.13"));
EXPECT_FALSE(CompareRuntimeVersion("", ""));
}
} // namespace tflite