blob: cc7df7ffd20b4ea393ec8d317ebd389859d182fe [file] [log] [blame]
"""Generate Flatbuffer binary from json."""
load(
"//tensorflow:tensorflow.bzl",
"tf_binary_additional_srcs",
"tf_cc_shared_object",
"tf_cc_test",
)
def tflite_copts():
"""Defines compile time flags."""
copts = [
"-DFARMHASH_NO_CXX_STRING",
] + select({
str(Label("//tensorflow:android_arm64")): [
"-O3",
],
str(Label("//tensorflow:android_arm")): [
"-mfpu=neon",
"-O3",
],
str(Label("//tensorflow:ios_x86_64")): [
"-msse4.1",
],
str(Label("//tensorflow:windows")): [
"/DTF_COMPILE_LIBRARY",
"/wd4018", # -Wno-sign-compare
],
"//conditions:default": [
"-Wno-sign-compare",
],
})
return copts
EXPORTED_SYMBOLS = "//tensorflow/lite/java/src/main/native:exported_symbols.lds"
LINKER_SCRIPT = "//tensorflow/lite/java/src/main/native:version_script.lds"
def tflite_linkopts_unstripped():
"""Defines linker flags to reduce size of TFLite binary.
These are useful when trying to investigate the relative size of the
symbols in TFLite.
Returns:
a select object with proper linkopts
"""
# In case you wonder why there's no --icf is because the gains were
# negligible, and created potential compatibility problems.
return select({
"//tensorflow:android": [
"-Wl,--no-export-dynamic", # Only inc syms referenced by dynamic obj.
"-Wl,--gc-sections", # Eliminate unused code and data.
"-Wl,--as-needed", # Don't link unused libs.
],
"//conditions:default": [],
})
def tflite_jni_linkopts_unstripped():
"""Defines linker flags to reduce size of TFLite binary with JNI.
These are useful when trying to investigate the relative size of the
symbols in TFLite.
Returns:
a select object with proper linkopts
"""
# In case you wonder why there's no --icf is because the gains were
# negligible, and created potential compatibility problems.
return select({
"//tensorflow:android": [
"-Wl,--gc-sections", # Eliminate unused code and data.
"-Wl,--as-needed", # Don't link unused libs.
],
"//conditions:default": [],
})
def tflite_symbol_opts():
"""Defines linker flags whether to include symbols or not."""
return select({
"//tensorflow:android": [
"-latomic", # Required for some uses of ISO C++11 <atomic> in x86.
],
"//conditions:default": [],
}) + select({
"//tensorflow:debug": [],
"//conditions:default": [
"-s", # Omit symbol table, for all non debug builds
],
})
def tflite_linkopts():
"""Defines linker flags to reduce size of TFLite binary."""
return tflite_linkopts_unstripped() + tflite_symbol_opts()
def tflite_jni_linkopts():
"""Defines linker flags to reduce size of TFLite binary with JNI."""
return tflite_jni_linkopts_unstripped() + tflite_symbol_opts()
def tflite_jni_binary(
name,
copts = tflite_copts(),
linkopts = tflite_jni_linkopts(),
linkscript = LINKER_SCRIPT,
exported_symbols = EXPORTED_SYMBOLS,
linkshared = 1,
linkstatic = 1,
testonly = 0,
deps = [],
tags = [],
srcs = []):
"""Builds a jni binary for TFLite."""
linkopts = linkopts + select({
"//tensorflow:macos": [
"-Wl,-exported_symbols_list,$(location {})".format(exported_symbols),
"-Wl,-install_name,@rpath/" + name,
],
"//tensorflow:windows": [],
"//conditions:default": [
"-Wl,--version-script,$(location {})".format(linkscript),
"-Wl,-soname," + name,
],
})
native.cc_binary(
name = name,
copts = copts,
linkshared = linkshared,
linkstatic = linkstatic,
deps = deps + [linkscript, exported_symbols],
srcs = srcs,
tags = tags,
linkopts = linkopts,
testonly = testonly,
)
def tflite_cc_shared_object(
name,
copts = tflite_copts(),
linkopts = [],
linkstatic = 1,
deps = [],
visibility = None):
"""Builds a shared object for TFLite."""
tf_cc_shared_object(
name = name,
copts = copts,
linkstatic = linkstatic,
linkopts = linkopts + tflite_jni_linkopts(),
framework_so = [],
deps = deps,
visibility = visibility,
)
def tf_to_tflite(name, src, options, out):
"""Convert a frozen tensorflow graphdef to TF Lite's flatbuffer.
Args:
name: Name of rule.
src: name of the input graphdef file.
options: options passed to TOCO.
out: name of the output flatbuffer file.
"""
toco_cmdline = " ".join([
"$(location //tensorflow/lite/toco:toco)",
"--input_format=TENSORFLOW_GRAPHDEF",
"--output_format=TFLITE",
("--input_file=$(location %s)" % src),
("--output_file=$(location %s)" % out),
] + options)
native.genrule(
name = name,
srcs = [src],
outs = [out],
cmd = toco_cmdline,
tools = ["//tensorflow/lite/toco:toco"] + tf_binary_additional_srcs(),
)
def tflite_to_json(name, src, out):
"""Convert a TF Lite flatbuffer to JSON.
Args:
name: Name of rule.
src: name of the input flatbuffer file.
out: name of the output JSON file.
"""
flatc = "@flatbuffers//:flatc"
schema = "//tensorflow/lite/schema:schema.fbs"
native.genrule(
name = name,
srcs = [schema, src],
outs = [out],
cmd = ("TMP=`mktemp`; cp $(location %s) $${TMP}.bin &&" +
"$(location %s) --raw-binary --strict-json -t" +
" -o /tmp $(location %s) -- $${TMP}.bin &&" +
"cp $${TMP}.json $(location %s)") %
(src, flatc, schema, out),
tools = [flatc],
)
def json_to_tflite(name, src, out):
"""Convert a JSON file to TF Lite's flatbuffer.
Args:
name: Name of rule.
src: name of the input JSON file.
out: name of the output flatbuffer file.
"""
flatc = "@flatbuffers//:flatc"
schema = "//tensorflow/lite/schema:schema_fbs"
native.genrule(
name = name,
srcs = [schema, src],
outs = [out],
cmd = ("TMP=`mktemp`; cp $(location %s) $${TMP}.json &&" +
"$(location %s) --raw-binary --unknown-json --allow-non-utf8 -b" +
" -o /tmp $(location %s) $${TMP}.json &&" +
"cp $${TMP}.bin $(location %s)") %
(src, flatc, schema, out),
tools = [flatc],
)
# This is the master list of generated examples that will be made into tests. A
# function called make_XXX_tests() must also appear in generate_examples.py.
# Disable a test by adding it to the blacklists specified in
# generated_test_models_failing().
def generated_test_models():
return [
"abs",
"add",
"add_n",
"arg_min_max",
"avg_pool",
"batch_to_space_nd",
"cast",
"ceil",
"concat",
"constant",
"control_dep",
"conv",
"conv2d_transpose",
"conv_with_shared_weights",
"conv_to_depthwiseconv_with_shared_weights",
"cos",
"depthwiseconv",
"div",
"elu",
"equal",
"exp",
"embedding_lookup",
"expand_dims",
"eye",
"fill",
"floor",
"floor_div",
"floor_mod",
"fully_connected",
"fused_batch_norm",
"gather",
"gather_nd",
"gather_with_constant",
"global_batch_norm",
"greater",
"greater_equal",
"identity",
"sum",
"l2norm",
"l2norm_shared_epsilon",
"l2_pool",
"leaky_relu",
"less",
"less_equal",
"local_response_norm",
"log_softmax",
"log",
"logical_and",
"logical_or",
"logical_xor",
"lstm",
"matrix_diag",
"matrix_set_diag",
"max_pool",
"maximum",
"mean",
"minimum",
"mirror_pad",
"mul",
"neg",
"not_equal",
"one_hot",
"pack",
"pad",
"padv2",
"placeholder_with_default",
"prelu",
"pow",
"range",
"rank",
"reduce_any",
"reduce_max",
"reduce_min",
"reduce_prod",
"relu",
"relu1",
"relu6",
"reshape",
"resize_bilinear",
"resolve_constant_strided_slice",
"reverse_sequence",
"reverse_v2",
"rfft2d",
"round",
"rsqrt",
"shape",
"sigmoid",
"sin",
"slice",
"softmax",
"space_to_batch_nd",
"space_to_depth",
"sparse_to_dense",
"split",
"splitv",
"sqrt",
"square",
"squared_difference",
"squeeze",
"strided_slice",
"strided_slice_1d_exhaustive",
"strided_slice_np_style",
"sub",
"tile",
"topk",
"transpose",
"transpose_conv",
"unfused_gru",
"unidirectional_sequence_lstm",
"unidirectional_sequence_rnn",
"unique",
"unpack",
"unroll_batch_matmul",
"where",
"zeros_like",
]
# List of models that fail generated tests for the conversion mode.
# If you have to disable a test, please add here with a link to the appropriate
# bug or issue.
def generated_test_models_failing(conversion_mode):
if conversion_mode == "toco-flex":
return [
"lstm", # TODO(b/117510976): Restore when lstm flex conversion works.
"unidirectional_sequence_lstm",
"unidirectional_sequence_rnn",
]
elif conversion_mode == "forward-compat":
return []
return []
def generated_test_conversion_modes():
"""Returns a list of conversion modes."""
return ["toco-flex", "forward-compat", ""]
def generated_test_models_all():
"""Generates a list of all tests with the different converters.
Returns:
List of tuples representing:
(conversion mode, name of test, test tags, test args).
"""
conversion_modes = generated_test_conversion_modes()
tests = generated_test_models()
options = []
for conversion_mode in conversion_modes:
failing_tests = generated_test_models_failing(conversion_mode)
for test in tests:
tags = []
args = []
if test in failing_tests:
tags.append("notap")
tags.append("manual")
if conversion_mode:
test += "_%s" % conversion_mode
# Flex conversion shouldn't suffer from the same conversion bugs
# listed for the default TFLite kernel backend.
if conversion_mode == "toco-flex":
args.append("--ignore_known_bugs=false")
options.append((conversion_mode, test, tags, args))
return options
def gen_zip_test(name, test_name, conversion_mode, **kwargs):
"""Generate a zipped-example test and its dependent zip files.
Args:
name: str. Resulting cc_test target name
test_name: str. Test targets this model. Comes from the list above.
conversion_mode: str. Which conversion mode to run with. Comes from the
list above.
**kwargs: tf_cc_test kwargs
"""
toco = "//tensorflow/lite/toco:toco"
flags = ""
if conversion_mode == "toco-flex":
flags += " --ignore_converter_errors --run_with_flex"
elif conversion_mode == "forward-compat":
flags += " --make_forward_compat_test"
gen_zipped_test_file(
name = "zip_%s" % test_name,
file = "%s.zip" % test_name,
toco = toco,
flags = flags + " --save_graphdefs",
)
tf_cc_test(name, **kwargs)
def gen_zipped_test_file(name, file, toco, flags):
"""Generate a zip file of tests by using :generate_examples.
Args:
name: str. Name of output. We will produce "`file`.files" as a target.
file: str. The name of one of the generated_examples targets, e.g. "transpose"
toco: str. Pathname of toco binary to run
flags: str. Any additional flags to include
"""
native.genrule(
name = file + ".files",
cmd = (("$(locations :generate_examples) --toco $(locations {0}) " +
" --zip_to_output {1} {2} $(@D)").format(toco, file, flags)),
outs = [file],
tools = [
":generate_examples",
toco,
],
)
native.filegroup(
name = name,
srcs = [file],
)
def gen_selected_ops(name, model):
"""Generate the library that includes only used ops.
Args:
name: Name of the generated library.
model: TFLite model to interpret.
"""
out = name + "_registration.cc"
tool = "//tensorflow/lite/tools:generate_op_registrations"
tflite_path = "//tensorflow/lite"
native.genrule(
name = name,
srcs = [model],
outs = [out],
cmd = ("$(location %s) --input_model=$(location %s) --output_registration=$(location %s) --tflite_path=%s") %
(tool, model, out, tflite_path[2:]),
tools = [tool],
)
def flex_dep(target_op_sets):
if "SELECT_TF_OPS" in target_op_sets:
return ["//tensorflow/lite/delegates/flex:delegate"]
else:
return []
def gen_model_coverage_test(src, model_name, data, failure_type, tags):
"""Generates Python test targets for testing TFLite models.
Args:
src: Main source file.
model_name: Name of the model to test (must be also listed in the 'data'
dependencies)
data: List of BUILD targets linking the data.
failure_type: List of failure types (none, toco, crash, inference)
expected for the corresponding combinations of op sets
("TFLITE_BUILTINS", "TFLITE_BUILTINS,SELECT_TF_OPS", "SELECT_TF_OPS").
tags: List of strings of additional tags.
"""
i = 0
for target_op_sets in ["TFLITE_BUILTINS", "TFLITE_BUILTINS,SELECT_TF_OPS", "SELECT_TF_OPS"]:
args = []
if failure_type[i] != "none":
args.append("--failure_type=%s" % failure_type[i])
i = i + 1
native.py_test(
name = "model_coverage_test_%s_%s" % (model_name, target_op_sets.lower().replace(",", "_")),
srcs = [src],
main = src,
size = "large",
args = [
"--model_name=%s" % model_name,
"--target_ops=%s" % target_op_sets,
] + args,
data = data,
srcs_version = "PY2AND3",
python_version = "PY2",
tags = [
"no_oss",
"no_windows",
] + tags,
deps = [
"//tensorflow/lite/testing/model_coverage:model_coverage_lib",
"//tensorflow/lite/python:lite",
"//tensorflow/python:client_testlib",
] + flex_dep(target_op_sets),
)