10b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
20b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
30b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleLicensed under the Apache License, Version 2.0 (the "License");
40b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selleyou may not use this file except in compliance with the License.
50b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleYou may obtain a copy of the License at
60b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
70b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle    http://www.apache.org/licenses/LICENSE-2.0
80b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
90b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleUnless required by applicable law or agreed to in writing, software
100b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selledistributed under the License is distributed on an "AS IS" BASIS,
110b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
120b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleSee the License for the specific language governing permissions and
130b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellelimitations under the License.
140b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle==============================================================================*/
150b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/builtin_op_data.h"
160b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/context.h"
170b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/internal/optimized/optimized_ops.h"
180b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/internal/quantization_util.h"
190b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h"
200b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/internal/tensor.h"
210b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/kernel_util.h"
220b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#include "tensorflow/contrib/lite/kernels/op_macros.h"
230b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
240b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellenamespace tflite {
250b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellenamespace ops {
260b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellenamespace builtin {
270b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellenamespace add {
280b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
290b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle// This file has three implementation of Add.
300b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selleenum KernelType {
310b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  kReference,
320b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  kGenericOptimized,  // Neon-free
330b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  kNeonOptimized,
340b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle};
350b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
360b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selleconstexpr int kInputTensor1 = 0;
370b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selleconstexpr int kInputTensor2 = 1;
380b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selleconstexpr int kOutputTensor = 0;
390b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
4036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlowerstruct OpData {
4136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  bool requires_broadcast;
4236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower};
4336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower
4436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlowervoid* Init(TfLiteContext* context, const char* buffer, size_t length) {
4536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  auto* data = new OpData;
4636f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  data->requires_broadcast = false;
4736f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  return data;
4836f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower}
4936f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower
5036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlowervoid Free(TfLiteContext* context, void* buffer) {
5136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  delete reinterpret_cast<OpData*>(buffer);
5236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower}
5336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower
540b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
5536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  OpData* data = reinterpret_cast<OpData*>(node->user_data);
5636f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower
570b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
580b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
590b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
600b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* input1 = GetInput(context, node, kInputTensor1);
610b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* input2 = GetInput(context, node, kInputTensor2);
620b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
630b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
6436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  TF_LITE_ENSURE_EQ(context, input1->type, input2->type);
6536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  output->type = input2->type;
6636f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower
6736f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  data->requires_broadcast = !HaveSameShapes(input1, input2);
680b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
6936f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  TfLiteIntArray* output_size = nullptr;
7036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  if (data->requires_broadcast) {
7136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    TF_LITE_ENSURE_OK(context, CalculateShapeForBroadcast(
7236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                                   context, input1, input2, &output_size));
7336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  } else {
7436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    output_size = TfLiteIntArrayCopy(input1->dims);
7536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  }
760b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
770b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return context->ResizeTensor(context, output, output_size);
780b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
790b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
800b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selletemplate <KernelType kernel_type>
810b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellevoid EvalAddFloat(TfLiteContext* context, TfLiteNode* node,
8236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                  TfLiteAddParams* params, const OpData* data,
8336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                  TfLiteTensor* input1, TfLiteTensor* input2,
8436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                  TfLiteTensor* output) {
850b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  float output_activation_min, output_activation_max;
860b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  CalculateActivationRangeFloat(params->activation, &output_activation_min,
870b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                &output_activation_max);
8836f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower#define TF_LITE_ADD(type, opname)                                   \
8936f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  type::opname(GetTensorData<float>(input1), GetTensorDims(input1), \
9036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               GetTensorData<float>(input2), GetTensorDims(input2), \
9136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               output_activation_min, output_activation_max,        \
9236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               GetTensorData<float>(output), GetTensorDims(output))
934463d105a8a4a83642b9709ba79310e8f4ddf577A. Unique TensorFlower  if (kernel_type == kReference) {
9436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    if (data->requires_broadcast) {
9536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower      TF_LITE_ADD(reference_ops, BroadcastAdd);
9636f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    } else {
9736f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower      TF_LITE_ADD(reference_ops, Add);
9836f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    }
994463d105a8a4a83642b9709ba79310e8f4ddf577A. Unique TensorFlower  } else {
10036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    if (data->requires_broadcast) {
10136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower      TF_LITE_ADD(optimized_ops, BroadcastAdd);
10236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    } else {
10336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower      TF_LITE_ADD(optimized_ops, Add);
10436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    }
1050b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  }
1060b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#undef TF_LITE_ADD
1070b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
1080b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1090b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selletemplate <KernelType kernel_type>
1100b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Sellevoid EvalAddQuantized(TfLiteContext* context, TfLiteNode* node,
11136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                      TfLiteAddParams* params, const OpData* data,
11236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                      TfLiteTensor* input1, TfLiteTensor* input2,
11336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                      TfLiteTensor* output) {
1140b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  auto input1_offset = -input1->params.zero_point;
1150b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  auto input2_offset = -input2->params.zero_point;
1160b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  auto output_offset = output->params.zero_point;
1170b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  const int left_shift = 20;
1180b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  const double twice_max_input_scale =
1190b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle      2 * std::max(input1->params.scale, input2->params.scale);
1200b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  const double real_input1_multiplier =
1210b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle      input1->params.scale / twice_max_input_scale;
1220b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  const double real_input2_multiplier =
1230b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle      input2->params.scale / twice_max_input_scale;
1240b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  const double real_output_multiplier =
1250b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle      twice_max_input_scale / ((1 << left_shift) * output->params.scale);
1260b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1270b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int32 input1_multiplier;
1280b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int input1_shift;
1290b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  QuantizeMultiplierSmallerThanOne(real_input1_multiplier, &input1_multiplier,
1300b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                   &input1_shift);
1310b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int32 input2_multiplier;
1320b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int input2_shift;
1330b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  QuantizeMultiplierSmallerThanOne(real_input2_multiplier, &input2_multiplier,
1340b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                   &input2_shift);
1350b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int32 output_multiplier;
1360b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int output_shift;
1370b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  QuantizeMultiplierSmallerThanOne(real_output_multiplier, &output_multiplier,
1380b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                   &output_shift);
1390b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1400b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  int32 output_activation_min, output_activation_max;
1410b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  CalculateActivationRangeUint8(params->activation, output,
1420b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                &output_activation_min, &output_activation_max);
1430b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
14436f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower#define TF_LITE_ADD(type, opname)                                            \
14536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  type::opname(left_shift, GetTensorData<uint8_t>(input1),                   \
14636f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               GetTensorDims(input1), input1_offset, input1_multiplier,      \
14736f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               input1_shift, GetTensorData<uint8_t>(input2),                 \
14836f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               GetTensorDims(input2), input2_offset, input2_multiplier,      \
14936f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               input2_shift, output_offset, output_multiplier, output_shift, \
15036f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               output_activation_min, output_activation_max,                 \
15136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower               GetTensorData<uint8_t>(output), GetTensorDims(output));
15236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  // The quantized version of Add doesn't support activations, so we
15336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  // always use BroadcastAdd.
1540b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  if (kernel_type == kReference) {
15536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    TF_LITE_ADD(reference_ops, BroadcastAdd);
1560b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  } else {
15736f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    TF_LITE_ADD(optimized_ops, BroadcastAdd);
1580b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  }
1590b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#undef TF_LITE_ADD
1600b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
1610b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1620b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selletemplate <KernelType kernel_type>
1630b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
1640b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  auto* params = reinterpret_cast<TfLiteAddParams*>(node->builtin_data);
16536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  OpData* data = reinterpret_cast<OpData*>(node->user_data);
1660b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1670b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* input1 = GetInput(context, node, kInputTensor1);
1680b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* input2 = GetInput(context, node, kInputTensor2);
1690b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
1700b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1710b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  if (output->type == kTfLiteFloat32) {
17236f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    EvalAddFloat<kernel_type>(context, node, params, data, input1, input2,
17336f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower                              output);
1740b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  } else if (output->type == kTfLiteUInt8) {
17536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower    EvalAddQuantized<kernel_type>(context, node, params, data, input1, input2,
1760b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                  output);
1770b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  } else {
1780b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle    context->ReportError(context,
1790b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                         "Inputs and outputs not all float|unit8 types.");
1800b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle    return kTfLiteError;
1810b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  }
1820b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1830b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return kTfLiteOk;
1840b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
1850b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1860b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}  // namespace add
1870b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1880b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteRegistration* Register_ADD_REF() {
18936f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  static TfLiteRegistration r = {add::Init, add::Free, add::Prepare,
1900b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                 add::Eval<add::kReference>};
1910b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return &r;
1920b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
1930b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
1940b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteRegistration* Register_ADD_GENERIC_OPT() {
19536f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  static TfLiteRegistration r = {add::Init, add::Free, add::Prepare,
1960b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                 add::Eval<add::kGenericOptimized>};
1970b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return &r;
1980b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
1990b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
2000b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteRegistration* Register_ADD_NEON_OPT() {
20136f3a3b31ea9c32c64f1f4af543c75692d338876A. Unique TensorFlower  static TfLiteRegistration r = {add::Init, add::Free, add::Prepare,
2020b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle                                 add::Eval<add::kNeonOptimized>};
2030b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return &r;
2040b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
2050b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
2060b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew SelleTfLiteRegistration* Register_ADD() {
2070b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#ifdef USE_NEON
2080b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return Register_ADD_NEON_OPT();
2090b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#else
2100b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle  return Register_ADD_GENERIC_OPT();
2110b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle#endif
2120b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}
2130b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle
2140b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}  // namespace builtin
2150b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}  // namespace ops
2160b15439f8f0f2d4755587f4096c3ea04cb199d23Andrew Selle}  // namespace tflite
217