blob: 8a9409df6a013331079f0f39f26baf82410d2e54 [file] [log] [blame]
Matthew Sloyan11572322023-03-16 10:17:51 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include <armnn_delegate.hpp>
9#include <DelegateUtils.hpp>
10
11#include <armnn/ArmNN.hpp>
12#include <armnn/BackendHelper.hpp>
Mike Kelly07169c82023-08-02 13:23:09 +010013#include <armnn/TypesUtils.hpp>
Matthew Sloyan11572322023-03-16 10:17:51 +000014#include <armnn/utility/Assert.hpp>
15#include <armnn/utility/NumericCast.hpp>
16
17#include <armnnUtils/Permute.hpp>
18#include <armnnUtils/TensorUtils.hpp>
19
20#include <tensorflow/lite/builtin_ops.h>
21#include <tensorflow/lite/c/builtin_op_data.h>
22#include <tensorflow/lite/c/common.h>
23#include <tensorflow/lite/minimal_logging.h>
24#include <tensorflow/lite/kernels/kernel_util.h>
25
Mike Kelly07169c82023-08-02 13:23:09 +010026#include <fmt/format.h>
27
Matthew Sloyan11572322023-03-16 10:17:51 +000028namespace
29{
30
31// Macro to call an Is<layer_name>Supported function and log caller name together with reason for lack of support
32#define FORWARD_LAYER_SUPPORT_FUNC(opName, tfLiteContext, func, backends, supported, setBackend, ...) \
33try \
34{ \
35 for (auto&& backendId : backends) \
36 { \
37 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
38 if (layerSupportObject.IsBackendRegistered()) \
39 { \
40 std::string reasonIfUnsupported; \
41 supported = \
42 layerSupportObject.func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
43 if (supported) \
44 { \
45 setBackend = backendId; \
46 break; \
47 } \
48 else \
49 { \
50 if (reasonIfUnsupported.size() > 0) \
51 { \
52 TFLITE_LOG_PROD(tflite::TFLITE_LOG_WARNING, \
53 "%s: not supported by armnn: %s", opName, reasonIfUnsupported.c_str()); \
54 } \
55 else \
56 { \
57 TFLITE_LOG_PROD(tflite::TFLITE_LOG_WARNING, \
58 "%s: not supported by armnn", opName); \
59 } \
60 } \
61 } \
62 else \
63 { \
64 TF_LITE_KERNEL_LOG(tfLiteContext, "%s: backend not registered: %s", opName, backendId.Get().c_str()); \
65 } \
66 } \
67 if (!supported) \
68 { \
69 TF_LITE_KERNEL_LOG(tfLiteContext, "%s: not supported by any specified backend", opName); \
70 } \
71} \
72catch (const armnn::InvalidArgumentException &e) \
73{ \
74 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
75}
76
Mike Kelly07169c82023-08-02 13:23:09 +010077std::string GetLayerName(armnn::ActivationFunction function, int nodeIndex)
78{
79 return fmt::format("{}:{}", GetActivationFunctionAsCString(function), nodeIndex);
80}
81
82std::string GetLayerName(armnn::ArgMinMaxFunction function, int nodeIndex)
83{
84 return fmt::format("{}:{}", GetArgMinMaxFunctionAsCString(function), nodeIndex);
85}
86
87std::string GetLayerName(armnn::BinaryOperation opType, int nodeIndex)
88{
89 return fmt::format("{}:{}", GetBinaryOperationAsCString(opType), nodeIndex);
90}
91
92std::string GetLayerName(armnn::ComparisonOperation layerType, int nodeIndex)
93{
94 return fmt::format("{}:{}", GetComparisonOperationAsCString(layerType), nodeIndex);
95}
96
97std::string GetLayerName(armnn::LogicalBinaryOperation operation, int nodeIndex)
98{
99 return fmt::format("{}:{}", GetLogicalBinaryOperationAsCString(operation), nodeIndex);
100}
101
102std::string GetLayerName(armnn::UnaryOperation opType, int nodeIndex)
103{
104 return fmt::format("{}:{}", GetUnaryOperationAsCString(opType), nodeIndex);
105}
106
107std::string GetLayerName(armnn::LayerType layerType, int nodeIndex, std::string name = "")
108{
109 return fmt::format("{}{}:{}", GetLayerTypeAsCString(layerType), name, nodeIndex);
110}
111
Matthew Sloyan11572322023-03-16 10:17:51 +0000112TfLiteStatus ValidateNumInputs(TfLiteContext* tfLiteContext,
113 TfLiteNode* tfLiteNode,
114 const unsigned int expectedSize,
115 int nodeIndex)
116{
117 auto numInputs = tfLiteNode->inputs->size;
118 if (static_cast<unsigned int >(numInputs) != expectedSize)
119 {
120 TF_LITE_MAYBE_KERNEL_LOG(
121 tfLiteContext, "TfLiteArmnnDelegate: Unexpected number of inputs (%d != %d) in node #%d",
122 numInputs, expectedSize, nodeIndex);
123 return kTfLiteError;
124 }
125 return kTfLiteOk;
126}
127
128TfLiteStatus ValidateNumOutputs(TfLiteContext* tfLiteContext,
129 TfLiteNode* tfLiteNode,
130 const unsigned int expectedSize,
131 int nodeIndex)
132{
133 auto numOutputs = tfLiteNode->outputs->size;
134 if (static_cast<unsigned int >(numOutputs) != expectedSize)
135 {
136 TF_LITE_MAYBE_KERNEL_LOG(
137 tfLiteContext, "TfLiteArmnnDelegate: Unexpected number of outputs (%d != %d) in node #%d",
138 numOutputs, expectedSize, nodeIndex);
139 return kTfLiteError;
140 }
141 return kTfLiteOk;
142}
143
144bool IsDynamicTensor(const TfLiteTensor& tfLiteTensor)
145{
146 auto tensorAllocationType = tfLiteTensor.allocation_type;
147 if (tensorAllocationType == kTfLiteDynamic)
148 {
149 return true;
150 }
151 return false;
152}
153
154bool IsValid(const TfLiteTensor* tfLiteTensor)
155{
156 return tfLiteTensor == nullptr ? false : true;
157}
158
159bool IsValid(TfLiteContext* tfLiteContext, const TfLiteTensor& tfLiteTensor, int32_t operatorCode, int32_t nodeIndex)
160{
161 if(!IsValid(&tfLiteTensor))
162 {
163 std::cout << "..Is Not Valid" << std::endl;
164 TF_LITE_MAYBE_KERNEL_LOG(
165 tfLiteContext,
166 "TfLiteArmnnDelegate: Invalid TfLite tensor in operator #%d node #%d: ",
167 operatorCode, nodeIndex);
168 return false;
169 }
170 if (IsDynamicTensor(tfLiteTensor))
171 {
172 std::cout << "..IsDynamicTensor" << std::endl;
173 TF_LITE_MAYBE_KERNEL_LOG(
174 tfLiteContext,
175 "TfLiteArmnnDelegate: Dynamic tensors are not supported in operator #%d node #%d: ",
176 operatorCode, nodeIndex);
177 return false;
178 }
179 return true;
180}
181
182bool IsAffineQuantization(const TfLiteTensor& tfLiteTensor)
183{
184 auto quantizationInfo = tfLiteTensor.quantization;
185 if (quantizationInfo.type == kTfLiteAffineQuantization)
186 {
187 return true;
188 }
189 return false;
190}
191
192TfLiteStatus Connect(armnn::IConnectableLayer* layer,
193 TfLiteNode* tfLiteNode,
194 armnnDelegate::DelegateData& data)
195{
196 ARMNN_ASSERT(static_cast<unsigned int>(tfLiteNode->outputs->size) == layer->GetNumOutputSlots());
197
198 // Connect the input slots
199 for (unsigned int inputIndex = 0; inputIndex < layer->GetNumInputSlots(); ++inputIndex)
200 {
201 if (data.m_OutputSlotForNode[tfLiteNode->inputs->data[inputIndex]] != nullptr)
202 {
203 data.m_OutputSlotForNode[tfLiteNode->inputs->data[inputIndex]]->Connect(layer->GetInputSlot(inputIndex));
204 }
205 }
206
207 // Prepare output slots
208 for (unsigned int outputIndex = 0; outputIndex < layer->GetNumOutputSlots(); ++outputIndex)
209 {
210 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(outputIndex);
211 data.m_OutputSlotForNode[static_cast<unsigned long>(tfLiteNode->outputs->data[outputIndex])] = &outputSlot;
212 }
213
214 return kTfLiteOk;
215}
216
217TfLiteStatus FusedActivation(TfLiteContext* tfLiteContext,
218 TfLiteNode* tfLiteNode,
219 TfLiteFusedActivation activationType,
220 armnn::IConnectableLayer* prevLayer,
221 unsigned int outputSlotIndex,
Mike Kelly07169c82023-08-02 13:23:09 +0100222 armnnDelegate::DelegateData& data,
223 int nodeIndex)
Matthew Sloyan11572322023-03-16 10:17:51 +0000224{
225
226 const armnn::TensorInfo& activationOutputInfo = prevLayer->GetOutputSlot(outputSlotIndex).GetTensorInfo();
227
228 armnn::ActivationDescriptor activationDesc;
229
230 switch (activationType)
231 {
232 case kTfLiteActNone:
233 {
234 // No Activation
235 return kTfLiteOk;
236 }
237 case kTfLiteActRelu:
238 {
239 activationDesc.m_Function = armnn::ActivationFunction::ReLu;
240 break;
241 }
242// The name of kTfLiteActRelu1 changed after TF Lite v2.3
243#if defined(ARMNN_POST_TFLITE_2_3)
244 case kTfLiteActReluN1To1:
245#else
246 case kTfLiteActRelu1:
247#endif
248 {
249 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
250 activationDesc.m_A = 1.0f;
251 activationDesc.m_B = -1.0f;
252 break;
253 }
254 case kTfLiteActRelu6:
255 {
256 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
257 activationDesc.m_A = 6.0f;
258 activationDesc.m_B = 0.0f;
259 break;
260 }
261 case kTfLiteActSigmoid:
262 {
263 activationDesc.m_Function = armnn::ActivationFunction::Sigmoid;
264 break;
265 }
266 case kTfLiteActTanh:
267 {
268 activationDesc.m_Function = armnn::ActivationFunction::TanH;
269 activationDesc.m_A = 1.0f;
270 activationDesc.m_B = 1.0f;
271 break;
272 }
273 default:
274 return kTfLiteError;
275 }
276
277 bool isSupported = false;
278 armnn::BackendId setBackend;
279 FORWARD_LAYER_SUPPORT_FUNC("ACTIVATION",
280 tfLiteContext,
281 IsActivationSupported,
282 data.m_Backends,
283 isSupported,
284 setBackend,
285 activationOutputInfo,
286 activationOutputInfo,
287 activationDesc);
288 if (!isSupported)
289 {
290 return kTfLiteError;
291 }
Mike Kelly07169c82023-08-02 13:23:09 +0100292 auto layerName = GetLayerName(activationDesc.m_Function, nodeIndex);
293 armnn::IConnectableLayer* activationLayer = data.m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Matthew Sloyan11572322023-03-16 10:17:51 +0000294 activationLayer->SetBackendId(setBackend);
295
296 ARMNN_ASSERT(activationLayer != nullptr);
297 activationLayer->GetOutputSlot(0).SetTensorInfo(activationOutputInfo);
298
299 // Connect and prepare output slots
300 for (unsigned int outputIndex = 0; outputIndex < activationLayer->GetNumOutputSlots(); ++outputIndex)
301 {
302 data.m_OutputSlotForNode[static_cast<unsigned long>(
303 tfLiteNode->outputs->data[outputIndex])]->Connect(activationLayer->GetInputSlot(0));
304 armnn::IOutputSlot& outputSlot = activationLayer->GetOutputSlot(outputIndex);
305 data.m_OutputSlotForNode[static_cast<unsigned long>(
306 tfLiteNode->outputs->data[outputIndex])] = &outputSlot;
307 }
308 return kTfLiteOk;
309}
310
311armnn::IConnectableLayer* AddReshapeLayer(TfLiteContext* tfLiteContext,
312 TfLiteNode* tfLiteNode,
313 armnn::IConnectableLayer* prevLayer,
314 armnn::TensorInfo reshapedOutputTensorInfo,
315 armnn::TensorInfo outputTensorInfo,
Mike Kelly07169c82023-08-02 13:23:09 +0100316 armnnDelegate::DelegateData& data,
317 int nodeIndex)
Matthew Sloyan11572322023-03-16 10:17:51 +0000318{
319 armnn::ReshapeDescriptor desc;
320 desc.m_TargetShape = outputTensorInfo.GetShape();
321
322 bool isSupported = false;
323 armnn::BackendId setBackend;
324 FORWARD_LAYER_SUPPORT_FUNC("RESHAPE",
325 tfLiteContext,
326 IsReshapeSupported,
327 data.m_Backends,
328 isSupported,
329 setBackend,
330 reshapedOutputTensorInfo,
331 outputTensorInfo,
332 desc);
333
334 if (!isSupported)
335 {
336 return nullptr;
337 }
338
Mike Kelly07169c82023-08-02 13:23:09 +0100339 auto layerName = GetLayerName(armnn::LayerType::Reshape, nodeIndex);
340 armnn::IConnectableLayer* reshapeLayer = data.m_Network->AddReshapeLayer(desc, layerName.c_str());
Matthew Sloyan11572322023-03-16 10:17:51 +0000341 reshapeLayer->SetBackendId(setBackend);
342 ARMNN_ASSERT(reshapeLayer != nullptr);
343
344 prevLayer->GetOutputSlot(0).SetTensorInfo(reshapedOutputTensorInfo);
345 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
346
347 // Connect and prepare output slots
348 for (unsigned int outputIndex = 0; outputIndex < reshapeLayer->GetNumOutputSlots(); ++outputIndex)
349 {
350 data.m_OutputSlotForNode[static_cast<unsigned long>(
351 tfLiteNode->outputs->data[outputIndex])]->Connect(reshapeLayer->GetInputSlot(0));
352 armnn::IOutputSlot& outputSlot = reshapeLayer->GetOutputSlot(outputIndex);
353 data.m_OutputSlotForNode[static_cast<unsigned long>(
354 tfLiteNode->outputs->data[outputIndex])] = &outputSlot;
355 }
356 return reshapeLayer;
357}
358
359armnn::DataType GetDataType(const TfLiteTensor& tfLiteTensor)
360{
361 switch (tfLiteTensor.type)
362 {
363 case kTfLiteBool:
364 return armnn::DataType::Boolean;
365 case kTfLiteFloat32:
366 return armnn::DataType::Float32;
367 case kTfLiteFloat16:
368 return armnn::DataType::Float16;
369 case kTfLiteUInt8:
370 return armnn::DataType::QAsymmU8;
371 case kTfLiteInt8:
372 {
373 auto quantizationInfo = tfLiteTensor.quantization;
374 if (quantizationInfo.type == kTfLiteAffineQuantization)
375 {
376 auto* quantization =
377 reinterpret_cast<TfLiteAffineQuantization*>(tfLiteTensor.quantization.params);
378 if (quantization->zero_point != nullptr && quantization->zero_point->size == 1)
379 {
380 return armnn::DataType::QAsymmS8;
381 }
382 else
383 {
384 return armnn::DataType::QSymmS8;
385 }
386 }
387 else
388 {
389 return armnn::DataType::QAsymmS8;
390 }
391 }
392 case kTfLiteInt16:
393 return armnn::DataType::QSymmS16;
394 case kTfLiteInt32:
395 return armnn::DataType::Signed32;
396 case kTfLiteInt64:
397 return armnn::DataType::Signed64;
398 default:
399 throw armnn::Exception(&"TfLiteArmnnDelegate: Unsupported data type: " [ tfLiteTensor.type]);
400 }
401}
402
403armnn::TensorInfo GetTensorInfoForTfLiteTensor(const TfLiteTensor& tfLiteTensor, bool isOutput = false)
404{
405 armnn::DataType type = GetDataType(tfLiteTensor);
406 armnn::TensorInfo ret;
407 auto tensorDimensionSize = tfLiteTensor.dims->size;
408 if (tensorDimensionSize == 0)
409 {
410 // If input tensor does not have a shape
411 // assuming that it has 1D tensor
412 if (!isOutput)
413 {
414 std::vector<unsigned int> safeShape = { 1 };
415 bool dimensionsSpecificity[1] = { true };
Mike Kelly460a1792023-08-01 11:31:55 +0100416 armnn::TensorShape tensorShape(safeShape.size(),
Matthew Sloyan11572322023-03-16 10:17:51 +0000417 safeShape.data(),
418 dimensionsSpecificity);
419 ret = armnn::TensorInfo(tensorShape, type);
420 if(tflite::IsConstantTensor(&tfLiteTensor))
421 {
422 ret.SetConstant(true);
423 }
424 }
425 else
426 {
427 armnn::TensorShape tensorShape(armnn::Dimensionality::NotSpecified);
428 ret = armnn::TensorInfo(tensorShape, type);
429 }
430 }
431 else
432 {
Mike Kelly460a1792023-08-01 11:31:55 +0100433 std::vector<unsigned int> tensorDims(tensorDimensionSize);
434 std::vector<unsigned char> dimensionsSpecificity(tensorDimensionSize, true);
435 for (int i = 0; i < tensorDimensionSize; ++i) {
Matthew Sloyan11572322023-03-16 10:17:51 +0000436 auto dim = tfLiteTensor.dims->data[i];
Mike Kelly460a1792023-08-01 11:31:55 +0100437 if (dim <= 0)
Matthew Sloyan11572322023-03-16 10:17:51 +0000438 {
439 dimensionsSpecificity[i] = false;
440 }
441 tensorDims[i] = static_cast<unsigned int>(dim);
442 }
Mike Kelly460a1792023-08-01 11:31:55 +0100443 armnn::TensorShape tensorShape(tensorDimensionSize,
Matthew Sloyan11572322023-03-16 10:17:51 +0000444 tensorDims.data(),
Mike Kelly460a1792023-08-01 11:31:55 +0100445 reinterpret_cast<const bool *>(dimensionsSpecificity.data()));
Matthew Sloyan11572322023-03-16 10:17:51 +0000446
Mike Kelly460a1792023-08-01 11:31:55 +0100447 if (tflite::IsConstantTensor(&tfLiteTensor))
Matthew Sloyan11572322023-03-16 10:17:51 +0000448 {
449 ret = armnn::TensorInfo(tensorShape, type);
450 ret.SetConstant(true);
451 }
452 else
453 {
454 ret = armnn::TensorInfo(tensorShape, type);
455 }
456 }
457
458 auto quantizationInfo = tfLiteTensor.quantization;
459 if (quantizationInfo.type == kTfLiteAffineQuantization)
460 {
461 // get per-channel quantization parameters
462 const auto* affineQuantization =
463 reinterpret_cast<TfLiteAffineQuantization*>(tfLiteTensor.quantization.params);
464 if (affineQuantization->scale->size > 1)
465 {
466 std::vector<float> quantizationScales;
467 for (unsigned int i = 0; i < static_cast<unsigned int>(affineQuantization->scale->size); ++i)
468 {
469 quantizationScales.push_back(affineQuantization->scale->data[i]);
470 }
471 ret.SetQuantizationScales(quantizationScales);
472 ret.SetQuantizationDim(armnn::numeric_cast<unsigned int>(affineQuantization->quantized_dimension));
473 }
474 else
475 {
476 ret.SetQuantizationScale(affineQuantization->scale->data[0]);
477 ret.SetQuantizationOffset(affineQuantization->zero_point->data[0]);
478 }
479 }
480 else
481 {
482 auto quantizationParameters = tfLiteTensor.params;
483 ret.SetQuantizationScale(quantizationParameters.scale);
484 ret.SetQuantizationOffset(quantizationParameters.zero_point);
485 }
486
487 return ret;
488}
489
490armnn::ConstTensor CreateConstTensor(const TfLiteTensor* tfLiteTensor,
491 const armnn::TensorInfo& tensorInfo)
492{
493 if (tfLiteTensor->allocation_type != kTfLiteMmapRo)
494 {
495 throw armnn::Exception(
496 "TfLiteArmnnDelegate: Not constant allocation type: " + std::to_string(tfLiteTensor->allocation_type));
497 }
498
499 return armnn::ConstTensor(tensorInfo, tfLiteTensor->data.data);
500}
501
502armnn::ConstTensor* GetConstTensorForTfLiteTensor(const TfLiteTensor* tfLiteTensors, TfLiteNode* tfLiteNode, int index)
503{
504 const TfLiteTensor &tfLiteTensor = tfLiteTensors[tfLiteNode->inputs->data[index]];
505 armnn::TensorInfo tensorInfo = GetTensorInfoForTfLiteTensor(tfLiteTensor);
506 return new armnn::ConstTensor(tensorInfo, tfLiteTensor.data.data);
507}
508
509bool IsOptionalOperandPresent(TfLiteNode* tfLiteNode, const int operandIndex)
510{
511 // If the inputs array has fewer than operandIndex entries or if the entry at operandIndex has a value of -1 or
512 // less then the input is not present.
513 if (tfLiteNode->inputs->size > operandIndex && tfLiteNode->inputs->data[operandIndex] >= 0)
514 {
515 return true;
516 }
517 return false;
518}
519
520TfLiteStatus ProcessInputs(armnn::IConnectableLayer* layer,
521 armnnDelegate::DelegateData& delegateData,
522 TfLiteContext* tfLiteContext,
Mike Kelly07169c82023-08-02 13:23:09 +0100523 TfLiteNode* tfLiteNode,
524 int nodeIndex)
Matthew Sloyan11572322023-03-16 10:17:51 +0000525{
526 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
527 // Process input tensors
528 // If input tensor is a Constant tensor create a constant layer and connect it to the network
529 for (unsigned int inputIndex = 0; inputIndex < layer->GetNumInputSlots(); ++inputIndex)
530 {
531 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[inputIndex]];
532 if (tflite::IsConstantTensor(&tfLiteInputTensor))
533 {
534 armnn::TensorInfo inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
535 bool isSupported = false;
536 armnn::BackendId setBackend;
537 FORWARD_LAYER_SUPPORT_FUNC("CONSTANT",
538 tfLiteContext,
539 IsConstantSupported,
540 delegateData.m_Backends,
541 isSupported,
542 setBackend,
543 inputTensorInfo);
544 if (!isSupported)
545 {
546 return kTfLiteError;
547 }
548 auto constantInput = CreateConstTensor(&tfLiteInputTensor,
549 inputTensorInfo);
Mike Kelly07169c82023-08-02 13:23:09 +0100550
551 auto layerName = GetLayerName(armnn::LayerType::Constant, nodeIndex);
552 armnn::IConnectableLayer* constantLayer = delegateData.m_Network->AddConstantLayer(constantInput,
553 layerName.c_str());
Matthew Sloyan11572322023-03-16 10:17:51 +0000554 constantLayer->SetBackendId(setBackend);
555 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
556 outputSlot.SetTensorInfo(inputTensorInfo);
557
558 delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[inputIndex]] = &outputSlot;
559 }
560 }
561 return kTfLiteOk;
562}
563
564} // namespace anonymous