Optimized Kelvin kernel for Reshape

- Reshape kernel that uses a Kelvin-specific memcpy implementation
- Bazel changes to select Kelvin kernels if the build configuration is
  correct

Change-Id: I21914f18c87b505f9fee765bfa96dd340b0b8c96
diff --git a/tensorflow/extra_rules.bzl b/tensorflow/extra_rules.bzl
index 9e20eda..c0a39dc 100644
--- a/tensorflow/extra_rules.bzl
+++ b/tensorflow/extra_rules.bzl
@@ -28,3 +28,7 @@
 def xtensa_vision_p6_config():
     """Config setting for all Vision P6 based cores."""
     return "//tensorflow/lite/micro/kernels:xtensa_vision_p6_default"
+
+def kelvin_config():
+    """Config setting for Kelvin-based cores."""
+    return "//tensorflow/lite/micro/kernels:kelvin_default"
diff --git a/tensorflow/lite/micro/kernels/BUILD b/tensorflow/lite/micro/kernels/BUILD
index 11ece2d..94a6d5b 100644
--- a/tensorflow/lite/micro/kernels/BUILD
+++ b/tensorflow/lite/micro/kernels/BUILD
@@ -6,6 +6,7 @@
     "xtensa_hifi_3z_config",
     "xtensa_hifi_5_config",
     "xtensa_vision_p6_config",
+    "kelvin_config",
 )
 
 package(
@@ -31,6 +32,10 @@
     packages = tflm_kernel_friends(),
 )
 
+exports_files(
+    glob(["*_test.cc"])
+)
+
 ####################################
 # C++ libraries
 ####################################
@@ -175,6 +180,9 @@
     "-DVISION_P6=1",
 ]
 
+KELVIN_COPTS = [
+]
+
 tflm_kernel_cc_library(
     name = "micro_ops",
     srcs = [
@@ -310,6 +318,7 @@
         xtensa_hifi_3z_config(): glob(["xtensa/**/*.h"]),
         xtensa_hifi_5_config(): glob(["xtensa/**/*.h"]),
         xtensa_vision_p6_config(): glob(["xtensa/**/*.h"]),
+        kelvin_config(): glob(["kelvin/**/*.h"]),
         "//conditions:default": [],
     }),
     accelerated_srcs = {
@@ -317,12 +326,14 @@
         xtensa_hifi_3z_config(): glob(["xtensa/**/*.cc"]),
         xtensa_hifi_5_config(): glob(["xtensa/**/*.cc"]),
         xtensa_vision_p6_config(): glob(["xtensa/**/*.cc"]),
+        kelvin_config(): glob(["kelvin/**/*.cc"]),
     },
     copts = micro_copts() + select({
         xtensa_fusion_f1_config(): HIFI4_COPTS,
         xtensa_hifi_3z_config(): HIFI4_COPTS,
         xtensa_hifi_5_config(): HIFI5_COPTS,
         xtensa_vision_p6_config(): VP6_COPTS,
+        kelvin_config(): KELVIN_COPTS,
         "//conditions:default": [],
     }),
     visibility = [
@@ -358,6 +369,7 @@
         xtensa_hifi_3z_config(): ["//third_party/xtensa/nnlib_hifi4:nnlib_hifi4_lib"],
         xtensa_hifi_5_config(): ["//third_party/xtensa/nnlib_hifi5:nnlib_hifi5_lib"],
         xtensa_vision_p6_config(): ["//third_party/xtensa/xi_tflmlib_vision_p6:xi_tflmlib_vision_p6_lib"],
+        kelvin_config(): ["@kelvin_sw//tflm/opt:opt"],
         "//conditions:default": [],
     }),
 )
@@ -1453,3 +1465,10 @@
         "cpu": "P6_200528",
     },
 )
+
+config_setting(
+    name = "kelvin_default",
+    values = {
+        "platforms": "//platforms/riscv32:kelvin"
+    },
+)
diff --git a/tensorflow/lite/micro/kernels/kelvin/reshape.cc b/tensorflow/lite/micro/kernels/kelvin/reshape.cc
new file mode 100644
index 0000000..9c88385
--- /dev/null
+++ b/tensorflow/lite/micro/kernels/kelvin/reshape.cc
@@ -0,0 +1,100 @@
+#include "tensorflow/lite/c/common.h"
+#include "tensorflow/lite/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/memory_helpers.h"
+#include "tensorflow/lite/micro/micro_utils.h"
+#include "tflm/opt/opt.h"
+
+namespace tflite {
+namespace {
+
+constexpr int kInputTensor = 0;
+constexpr int kOutputTensor = 0;
+
+TfLiteStatus ReshapeOutput(TfLiteContext* context, TfLiteNode* node) {
+  MicroContext* micro_context = GetMicroContext(context);
+
+  TfLiteTensor* input =
+      micro_context->AllocateTempInputTensor(node, kInputTensor);
+  TF_LITE_ENSURE(context, input != nullptr);
+  TfLiteTensor* output =
+      micro_context->AllocateTempOutputTensor(node, kOutputTensor);
+  TF_LITE_ENSURE(context, output != nullptr);
+  // Tensorflow's Reshape allows one of the shape components to have the
+  // special -1 value, meaning it will be calculated automatically based on the
+  // input. Here we calculate what that dimension should be so that the number
+  // of output elements in the same as the number of input elements.
+  int num_input_elements = NumElements(input);
+  TfLiteIntArray* output_shape = output->dims;
+
+  if (NumInputs(node) == 1 &&  // Legacy scalar supported with params.
+      output_shape->size == 1 && output_shape->data[0] == 0) {
+    // Legacy tflite models use a shape parameter of [0] to indicate scalars,
+    // so adjust accordingly. TODO(b/111614235): Allow zero-sized buffers during
+    // toco conversion.
+    output_shape->size = 0;
+  }
+
+  int num_output_elements = 1;
+  int stretch_dim = -1;
+  for (int i = 0; i < output_shape->size; ++i) {
+    int value = output_shape->data[i];
+    if (value == -1) {
+      TF_LITE_ENSURE_EQ(context, stretch_dim, -1);
+      stretch_dim = i;
+    } else {
+      num_output_elements *= value;
+    }
+  }
+  if (stretch_dim != -1) {
+    TfLiteEvalTensor* output_eval =
+        tflite::micro::GetEvalOutput(context, node, kOutputTensor);
+    TF_LITE_ENSURE_STATUS(tflite::micro::CreateWritableTensorDimsWithCopy(
+        context, output, output_eval));
+    output_shape = output->dims;  // output tensor dims were moved
+    output_shape->data[stretch_dim] = num_input_elements / num_output_elements;
+    num_output_elements *= output_shape->data[stretch_dim];
+  }
+
+  TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
+  TF_LITE_ENSURE_EQ(context, num_input_elements, num_output_elements);
+
+  micro_context->DeallocateTempTfLiteTensor(input);
+  micro_context->DeallocateTempTfLiteTensor(output);
+  return kTfLiteOk;
+}
+
+TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
+  TF_LITE_ENSURE(context, NumInputs(node) == 1 || NumInputs(node) == 2);
+  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
+  TF_LITE_ENSURE_EQ(context, ReshapeOutput(context, node), kTfLiteOk);
+  return kTfLiteOk;
+}
+
+TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
+  const TfLiteEvalTensor* input =
+      tflite::micro::GetEvalInput(context, node, kInputTensor);
+  TfLiteEvalTensor* output =
+      tflite::micro::GetEvalOutput(context, node, kOutputTensor);
+
+  // TODO(b/162522304): storing input bytes in OpData increases some models
+  // significantly, possibly due to alignment issues.
+  size_t input_bytes;
+  TF_LITE_ENSURE_STATUS(TfLiteTypeSizeOf(input->type, &input_bytes));
+  input_bytes *= ElementCount(*input->dims);
+
+  // Do nothing for in-place reshape.
+  if (input->data.raw != output->data.raw) {
+    // Otherwise perform reshape with copy.
+    kelvin::opt::memcpy(output->data.raw, input->data.raw, input_bytes);
+  }
+  return kTfLiteOk;
+}
+
+}  // namespace
+
+TFLMRegistration Register_RESHAPE() {
+  return tflite::micro::RegisterOp(nullptr, Prepare, Eval);
+}
+
+}  // namespace tflite