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