blob: 84486096959473ed2ca7636d37529d596726e695 [file] [log] [blame]
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
David Monahan6c53f9f2023-04-27 15:21:19 +01005#pragma once
6
7#include <OpaqueDelegateUtils.hpp>
8#include <MultiLayerFacade.hpp>
9
10
11namespace armnnOpaqueDelegate
12{
13
14TfLiteStatus ValidateAddOperator(DelegateData& delegateData,
15 TfLiteOpaqueContext* tfLiteContext,
16 const armnn::TensorInfo& inputInfo1,
17 const armnn::TensorInfo& inputInfo2,
18 const armnn::TensorInfo& outputInfo)
19{
20 bool isSupported = false;
21 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
22 {
23 std::vector<armnn::TensorInfo> infos { inputInfo1, inputInfo2, outputInfo };
24 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("ADD",
25 tfLiteContext,
26 IsElementwiseBinarySupported,
27 delegateData.m_Backends,
28 isSupported,
29 armnn::BackendId(),
30 inputInfo1,
31 inputInfo2,
32 outputInfo,
33 armnn::BinaryOperation::Add);
34 };
35
36 validateFunc(outputInfo, isSupported);
37 return isSupported ? kTfLiteOk : kTfLiteError;
38}
39
40
41TfLiteStatus ValidateDivOperator(DelegateData& delegateData,
42 TfLiteOpaqueContext* tfLiteContext,
43 const armnn::TensorInfo& inputInfo1,
44 const armnn::TensorInfo& inputInfo2,
45 const armnn::TensorInfo& outputInfo)
46{
47 bool isSupported = false;
48 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
49 {
50 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("DIV",
51 tfLiteContext,
52 IsElementwiseBinarySupported,
53 delegateData.m_Backends,
54 isSupported,
55 armnn::BackendId(),
56 inputInfo1,
57 inputInfo2,
58 outputTensorInfo,
59 armnn::BinaryOperation::Div);
60 };
61
62 validateFunc(outputInfo, isSupported);
63 return isSupported ? kTfLiteOk : kTfLiteError;
64}
65
66TfLiteStatus ValidateFloorDivOperator(DelegateData& delegateData,
67 TfLiteOpaqueContext* tfLiteContext,
68 const armnn::TensorInfo& inputInfo1,
69 const armnn::TensorInfo& inputInfo2,
70 const armnn::TensorInfo& outputInfo)
71{
72 // need first to validate that the div operator is supported
73 // then that the floor operator is supported
74 TfLiteStatus status = ValidateDivOperator(delegateData, tfLiteContext, inputInfo1, inputInfo2, outputInfo);
75 if (status != kTfLiteOk)
76 {
77 return status;
78 }
79 // if the inputs and output of the div are all Signed32 we don't need to add the floor operator afterward.
80 if (AreAllSigned32(inputInfo1, inputInfo2, outputInfo))
81 {
82 return status;
83 }
84 // in case broadcasting is being done from one of the inputs to the div
85 // choose the full sized input tensor to pass to the floor validation routine
86 armnn::TensorInfo floorInputInfo = inputInfo1;
87 if (inputInfo1.GetNumDimensions() < inputInfo2.GetNumDimensions())
88 {
89 floorInputInfo = inputInfo2;
90 }
91 status = ValidateFloorOperator(delegateData, tfLiteContext, floorInputInfo, outputInfo);
92 return status;
93}
94
95TfLiteStatus ValidateMaximumOperator(DelegateData& delegateData,
96 TfLiteOpaqueContext* tfLiteContext,
97 const armnn::TensorInfo& inputInfo1,
98 const armnn::TensorInfo& inputInfo2,
99 const armnn::TensorInfo& outputInfo)
100{
101 bool isSupported = false;
102 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
103 {
104 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("MAXIMUM",
105 tfLiteContext,
106 IsElementwiseBinarySupported,
107 delegateData.m_Backends,
108 isSupported,
109 armnn::BackendId(),
110 inputInfo1,
111 inputInfo2,
112 outputTensorInfo,
113 armnn::BinaryOperation::Maximum);
114 };
115
116 validateFunc(outputInfo, isSupported);
117 return isSupported ? kTfLiteOk : kTfLiteError;
118}
119
120TfLiteStatus ValidateMinimumOperator(DelegateData& delegateData,
121 TfLiteOpaqueContext* tfLiteContext,
122 const armnn::TensorInfo& inputInfo1,
123 const armnn::TensorInfo& inputInfo2,
124 const armnn::TensorInfo& outputInfo)
125{
126 bool isSupported = false;
127 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
128 {
129 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("MINIMUM",
130 tfLiteContext,
131 IsElementwiseBinarySupported,
132 delegateData.m_Backends,
133 isSupported,
134 armnn::BackendId(),
135 inputInfo1,
136 inputInfo2,
137 outputTensorInfo,
138 armnn::BinaryOperation::Minimum);
139 };
140
141 validateFunc(outputInfo, isSupported);
142 return isSupported ? kTfLiteOk : kTfLiteError;
143}
144
145TfLiteStatus ValidateMulOperator(DelegateData& delegateData,
146 TfLiteOpaqueContext* tfLiteContext,
147 const armnn::TensorInfo& inputInfo1,
148 const armnn::TensorInfo& inputInfo2,
149 const armnn::TensorInfo& outputInfo)
150{
151 bool isSupported = false;
152 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
153 {
154 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("MUL",
155 tfLiteContext,
156 IsElementwiseBinarySupported,
157 delegateData.m_Backends,
158 isSupported,
159 armnn::BackendId(),
160 inputInfo1,
161 inputInfo2,
162 outputTensorInfo,
163 armnn::BinaryOperation::Mul);
164 };
165
166 validateFunc(outputInfo, isSupported);
167 return isSupported ? kTfLiteOk : kTfLiteError;
168}
169
John Mcloughlin0ec00872023-05-15 17:03:49 +0100170TfLiteStatus ValidatePowerOperator(DelegateData& delegateData,
171 TfLiteOpaqueContext* tfLiteContext,
172 const armnn::TensorInfo& inputInfo1,
173 const armnn::TensorInfo& inputInfo2,
174 const armnn::TensorInfo& outputInfo)
175{
176 bool isSupported = false;
177 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
178 {
179 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("POWER",
180 tfLiteContext,
181 IsElementwiseBinarySupported,
182 delegateData.m_Backends,
183 isSupported,
184 armnn::BackendId(),
185 inputInfo1,
186 inputInfo2,
187 outputTensorInfo,
188 armnn::BinaryOperation::Power);
189 };
190
191 validateFunc(outputInfo, isSupported);
192 return isSupported ? kTfLiteOk : kTfLiteError;
193}
194
195TfLiteStatus ValidateSquaredDifferenceOperator(DelegateData& delegateData,
196 TfLiteOpaqueContext* tfLiteContext,
197 const armnn::TensorInfo& inputInfo1,
198 const armnn::TensorInfo& inputInfo2,
199 const armnn::TensorInfo& outputInfo)
200{
201 bool isSupported = false;
202 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
203 {
204 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("SQUAREDDIFFERENCE",
205 tfLiteContext,
206 IsElementwiseBinarySupported,
207 delegateData.m_Backends,
208 isSupported,
209 armnn::BackendId(),
210 inputInfo1,
211 inputInfo2,
212 outputTensorInfo,
213 armnn::BinaryOperation::SqDiff);
214 };
215
216 validateFunc(outputInfo, isSupported);
217 return isSupported ? kTfLiteOk : kTfLiteError;
218}
219
David Monahan6c53f9f2023-04-27 15:21:19 +0100220TfLiteStatus ValidateSubOperator(DelegateData& delegateData,
221 TfLiteOpaqueContext* tfLiteContext,
222 const armnn::TensorInfo& inputInfo1,
223 const armnn::TensorInfo& inputInfo2,
224 const armnn::TensorInfo& outputInfo)
225{
226 bool isSupported = false;
227 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
228 {
229 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("SUB",
230 tfLiteContext,
231 IsElementwiseBinarySupported,
232 delegateData.m_Backends,
233 isSupported,
234 armnn::BackendId(),
235 inputInfo1,
236 inputInfo2,
237 outputTensorInfo,
238 armnn::BinaryOperation::Sub);
239 };
240
241 validateFunc(outputInfo, isSupported);
242 return isSupported ? kTfLiteOk : kTfLiteError;
243}
244
245std::pair<armnn::IConnectableLayer*, armnn::IConnectableLayer*> AddFloorDivLayer(
246 DelegateData& delegateData,
247 const armnn::TensorInfo& outputTensorInfo)
248{
249 armnn::IConnectableLayer* divisionLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
250 armnn::BinaryOperation::Div);
251 // if the output of the div is Signed32 the Floor layer is not required
252 if (armnn::DataType::Signed32 == outputTensorInfo.GetDataType())
253 {
254 return std::make_pair(divisionLayer, divisionLayer);
255 }
256 armnn::IOutputSlot& outputSlot = divisionLayer->GetOutputSlot(0);
257 outputSlot.SetTensorInfo(outputTensorInfo);
258 armnn::IConnectableLayer* floorLayer = delegateData.m_Network->AddFloorLayer();
259 outputSlot.Connect(floorLayer->GetInputSlot(0));
260 return std::make_pair(divisionLayer, floorLayer);
261}
262
263TfLiteStatus VisitElementwiseBinaryOperator(DelegateData& delegateData,
264 TfLiteOpaqueContext* tfLiteContext,
265 TfLiteOpaqueNode* tfLiteNode,
266 int nodeIndex,
267 int32_t elementwiseBinaryOperatorCode)
268{
269 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
270 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
271
272 // Gather input indices and use to get Input Tensors
273 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
274 const int* inputTensors;
275 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
276 {
277 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
278 tfLiteContext,
279 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
280 nodeIndex);
281 return kTfLiteError;
282 }
283 const TfLiteOpaqueTensor* tfLiteInputTensor0 = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
284 if (!IsValid(tfLiteContext, tfLiteInputTensor0, elementwiseBinaryOperatorCode, nodeIndex))
285 {
286 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
287 tfLiteContext,
288 "TfLiteArmnnOpaqueDelegate: Invalid input tensor in operator #%d node #%d: ",
289 elementwiseBinaryOperatorCode, nodeIndex);
290 return kTfLiteError;
291 }
292 // Use input indices to get filter tensor.
293 const TfLiteOpaqueTensor* tfLiteInputTensor1 = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]);
294 if(!IsValid(tfLiteInputTensor1))
295 {
296 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
297 tfLiteContext,
298 "TfLiteArmnnOpaqueDelegate: Invalid input tensor in operator #%d node #%d: ",
299 elementwiseBinaryOperatorCode, nodeIndex);
300 return kTfLiteError;
301 }
302
303 // Gather output indices and use to get output tensors.
304 int numOutputs = 0;
305 const int* outputTensors;
306 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
307 {
308 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
309 tfLiteContext,
310 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
311 nodeIndex);
312 return kTfLiteError;
313 }
314 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
315 if (!IsValid(tfLiteContext, tfLiteOutputTensor, elementwiseBinaryOperatorCode, nodeIndex))
316 {
317 return kTfLiteError;
318 }
319
320 armnn::TensorInfo inputTensorInfo0 = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor0);
321 armnn::TensorInfo inputTensorInfo1 = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor1);
322 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
323
324
325
326 // Check if we need to expand the dims of the input tensor infos.
327 // This is required for a few of the backends.
328 if(inputTensorInfo0.GetNumDimensions() != inputTensorInfo1.GetNumDimensions())
329 {
330 ExpandTensorRankToEqual(inputTensorInfo0, inputTensorInfo1);
331 }
332
333 auto* tfLiteNodeParameters = reinterpret_cast<TfLiteAddParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode));
334 TfLiteFusedActivation activationType = kTfLiteActNone;
335 if (tfLiteNodeParameters)
336 {
337 activationType = tfLiteNodeParameters->activation;
338 TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData,
339 tfLiteContext,
340 outputTensorInfo,
341 outputTensorInfo,
342 activationType);
343 if(activationStatus != kTfLiteOk)
344 {
345 return kTfLiteError;
346 }
347 }
348
349 if (!delegateData.m_Network)
350 {
351 switch(elementwiseBinaryOperatorCode)
352 {
353 case kTfLiteBuiltinAdd:
354 return ValidateAddOperator(delegateData,
355 tfLiteContext,
356 inputTensorInfo0,
357 inputTensorInfo1,
358 outputTensorInfo);
359 case kTfLiteBuiltinDiv:
360 return ValidateDivOperator(delegateData,
361 tfLiteContext,
362 inputTensorInfo0,
363 inputTensorInfo1,
364 outputTensorInfo);
365 case kTfLiteBuiltinFloorDiv:
366 return ValidateFloorDivOperator(delegateData,
367 tfLiteContext,
368 inputTensorInfo0,
369 inputTensorInfo1,
370 outputTensorInfo);
371 case kTfLiteBuiltinMaximum:
372 return ValidateMaximumOperator(delegateData,
373 tfLiteContext,
374 inputTensorInfo0,
375 inputTensorInfo1,
376 outputTensorInfo);
377 case kTfLiteBuiltinMinimum:
378 return ValidateMinimumOperator(delegateData,
379 tfLiteContext,
380 inputTensorInfo0,
381 inputTensorInfo1,
382 outputTensorInfo);
383 case kTfLiteBuiltinMul:
384 return ValidateMulOperator(delegateData,
385 tfLiteContext,
386 inputTensorInfo0,
387 inputTensorInfo1,
388 outputTensorInfo);
John Mcloughlin0ec00872023-05-15 17:03:49 +0100389 case kTfLiteBuiltinPow:
390 return ValidatePowerOperator(delegateData,
391 tfLiteContext,
392 inputTensorInfo0,
393 inputTensorInfo1,
394 outputTensorInfo);
395 case kTfLiteBuiltinSquaredDifference:
396 return ValidateSquaredDifferenceOperator(delegateData,
397 tfLiteContext,
398 inputTensorInfo0,
399 inputTensorInfo1,
400 outputTensorInfo);
David Monahan6c53f9f2023-04-27 15:21:19 +0100401 case kTfLiteBuiltinSub:
402 return ValidateSubOperator(delegateData,
403 tfLiteContext,
404 inputTensorInfo0,
405 inputTensorInfo1,
406 outputTensorInfo);
407 default:
408 return kTfLiteError;
409 }
410 }
411
412 armnn::IConnectableLayer* elementwiseBinaryLayer = nullptr;
413 armnnDelegate::MultiLayerFacade multiLayer;
414 switch(elementwiseBinaryOperatorCode)
415 {
416 case kTfLiteBuiltinAdd:
417 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
418 armnn::BinaryOperation::Add);
419 break;
420 case kTfLiteBuiltinDiv:
421 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
422 armnn::BinaryOperation::Div);
423 break;
424 case kTfLiteBuiltinFloorDiv:
425 {
426 auto layers = AddFloorDivLayer(delegateData, outputTensorInfo);
427 multiLayer.AssignValues(layers.first, layers.second);
428 elementwiseBinaryLayer = &multiLayer;
429 }
430 break;
431 case kTfLiteBuiltinMaximum:
432 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
433 armnn::BinaryOperation::Maximum);
434 break;
435 case kTfLiteBuiltinMinimum:
436 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
437 armnn::BinaryOperation::Minimum);
438 break;
439 case kTfLiteBuiltinMul:
440 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
441 armnn::BinaryOperation::Mul);
442 break;
John Mcloughlin0ec00872023-05-15 17:03:49 +0100443 case kTfLiteBuiltinPow:
444 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
445 armnn::BinaryOperation::Power);
446 break;
447 case kTfLiteBuiltinSquaredDifference:
448 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
449 armnn::BinaryOperation::SqDiff);
450 break;
David Monahan6c53f9f2023-04-27 15:21:19 +0100451 case kTfLiteBuiltinSub:
452 elementwiseBinaryLayer = delegateData.m_Network->AddElementwiseBinaryLayer(
453 armnn::BinaryOperation::Sub);
454 break;
455 default:
456 return kTfLiteError;
457 }
458 ARMNN_ASSERT(elementwiseBinaryLayer != nullptr);
459 armnn::IOutputSlot& outputSlot = elementwiseBinaryLayer->GetOutputSlot(0);
460 outputSlot.SetTensorInfo(outputTensorInfo);
461
462 auto inputsTensorsProcess = ProcessInputs(elementwiseBinaryLayer,
463 delegateData,
464 tfLiteContext,
465 tfLiteNode);
466 if (inputsTensorsProcess == kTfLiteError)
467 {
468 return inputsTensorsProcess;
469 }
470
471 if(Connect(elementwiseBinaryLayer, tfLiteContext, tfLiteNode, delegateData) != kTfLiteOk)
472 {
473 return kTfLiteError;
474 }
475
476 if (!tfLiteNodeParameters)
477 {
478 // No Activation
479 return kTfLiteOk;
480 }
481 // Check and Create Activation
482 return FusedActivation(tfLiteContext, tfLiteNode, activationType, elementwiseBinaryLayer, 0, delegateData);
483}
484
485} // namespace armnnOpaqueDelegate