blob: fbc5e280d343784ed2e1199e980353f1ad03d77d [file] [log] [blame]
Sadik Armagan8f397a12022-06-17 15:38:22 +01001//
2// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "ConversionUtils.hpp"
7#include <armnnUtils/Permute.hpp>
8
9///
10/// Helper classes
11///
12
13namespace armnn_driver
14{
15
16LayerInputHandle::LayerInputHandle()
17 : m_OutputSlot(nullptr)
18 , m_Valid(false)
19{}
20
21LayerInputHandle::LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo)
22 : m_OutputSlot(outputSlot)
23 , m_Valid(valid)
24 , m_TensorInfo(tensorInfo)
25{}
26
27bool LayerInputHandle::IsValid() const
28{
29 return m_Valid;
30}
31
32void LayerInputHandle::Connect(armnn::IInputSlot& inputSlot)
33{
34 ARMNN_ASSERT(IsValid());
35 if (m_OutputSlot)
36 {
37 m_OutputSlot->Connect(inputSlot);
38 }
39}
40
41void LayerInputHandle::Disconnect(armnn::IInputSlot& inputSlot)
42{
43 ARMNN_ASSERT(IsValid());
44 if (m_OutputSlot)
45 {
46 m_OutputSlot->Disconnect(inputSlot);
47 }
48}
49
50const armnn::TensorInfo& LayerInputHandle::GetTensorInfo() const
51{
52 return m_TensorInfo;
53}
54
55void LayerInputHandle::SanitizeQuantizationScale(LayerInputHandle& weight, LayerInputHandle& input)
56{
57 if (m_OutputSlot)
58 {
59 armnn::TensorInfo weightInfo = weight.GetTensorInfo();
60 armnn::TensorInfo inputInfo = input.GetTensorInfo();
61 armnn::TensorInfo biasInfo = GetTensorInfo();
62
63 SanitizeBiasQuantizationScale(biasInfo, weightInfo, inputInfo);
64
65 m_TensorInfo = biasInfo;
66 m_OutputSlot->SetTensorInfo(biasInfo);
67 }
68}
69
70ConstTensorPin::ConstTensorPin(bool optional)
71 : m_Optional(optional)
72{}
73
74ConstTensorPin::ConstTensorPin(armnn::TensorInfo& tensorInfo,
75 const void* valueStart,
76 uint32_t numBytes,
77 const armnn::PermutationVector& mappings)
78 : m_Optional(false)
79{
80 armnn::IgnoreUnused(numBytes);
81 if (tensorInfo.GetNumBytes() != numBytes)
82 {
83 VLOG(DRIVER) << "The size of ConstTensor does not match its TensorInfo.";
84 }
85
86 const bool needsSwizzling = (mappings.GetSize() > 0);
87 if (needsSwizzling)
88 {
89 m_SwizzledTensorData.resize(tensorInfo.GetNumBytes());
90 SwizzleAndroidNn4dTensorToArmNn(tensorInfo, valueStart, m_SwizzledTensorData.data(), mappings);
91
92 m_ConstTensor = armnn::ConstTensor(tensorInfo, m_SwizzledTensorData.data());
93 }
94 else
95 {
96 m_ConstTensor = armnn::ConstTensor(tensorInfo, valueStart);
97 }
98}
99
100bool ConstTensorPin::IsValid() const
101{
102 return m_ConstTensor.GetMemoryArea() != nullptr;
103}
104
105bool ConstTensorPin::IsOptional() const
106{
107 return m_Optional;
108}
109
110const armnn::ConstTensor& ConstTensorPin::GetConstTensor() const
111{
112 return m_ConstTensor;
113}
114
115const armnn::ConstTensor* ConstTensorPin::GetConstTensorPtr() const
116{
117 if (IsValid() && m_ConstTensor.GetNumElements() > 0)
118 {
119 return &m_ConstTensor;
120 }
121 // tensor is either invalid, or has no elements (indicating an optional tensor that was not provided)
122 return nullptr;
123}
124
125///
126/// Utility functions
127///
128
129bool IsWeightsValid(const Operation& operation,
130 uint32_t inputIndex,
131 const Model& model)
132{
133 const Operand* operand = GetInputOperand(operation, inputIndex, model);
134 if (!operand)
135 {
136 Fail("%s: failed to get input operand %i", __func__, inputIndex);
137 return false;
138 }
139
140 if (operand->lifetime != OperandLifeTime::CONSTANT_COPY
141 && operand->lifetime != OperandLifeTime::CONSTANT_REFERENCE
142 && operand->lifetime != OperandLifeTime::NO_VALUE)
143 {
144 return false;
145 }
146 return true;
147}
148
149ConstTensorPin ConvertOperandToConstTensorPin(const Operand& operand,
150 const Model& model,
151 const ConversionData& data,
152 const armnn::PermutationVector& dimensionMappings,
153 const armnn::TensorShape* overrideTensorShape,
154 bool optional)
155{
156 if (!IsOperandTypeSupportedForTensors(operand.type))
157 {
158 VLOG(DRIVER) << __func__ << ": unsupported operand type for tensor" << operand.type;
159 return ConstTensorPin();
160 }
161
162 if (!optional && !IsOperandConstant(operand))
163 {
164 VLOG(DRIVER) << __func__ << ": lifetime for input tensor: r" << operand.lifetime;
165 return ConstTensorPin();
166 }
167
168 const void* const valueStart = GetOperandValueReadOnlyAddress(operand, model, data, optional);
169 if (!valueStart)
170 {
171 if (optional)
172 {
173 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
174 return ConstTensorPin(true);
175 }
176 // mandatory tensor with no values
177 Fail("%s: failed to get operand address", __func__);
178 return ConstTensorPin();
179 }
180
181 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
182
183 // Make sure isConstant flag is set.
184 tensorInfo.SetConstant();
185
186 if (overrideTensorShape != nullptr)
187 {
188 tensorInfo.SetShape(*overrideTensorShape);
189 }
190 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
191}
192
193LayerInputHandle ConvertToLayerInputHandle(const Operation& operation,
194 uint32_t inputIndex,
195 const Model& model,
196 ConversionData& data,
197 const armnn::PermutationVector& dimensionMappings)
198{
199
200 const Operand* operand = GetInputOperand(operation, inputIndex, model);
201 if (!operand)
202 {
203 Fail("%s: failed to get input operand %i", __func__, inputIndex);
204 return LayerInputHandle();
205 }
206
207 if (!IsOperandTypeSupportedForTensors(operand->type))
208 {
209 VLOG(DRIVER) << __func__ << ": unsupported operand type for tensor: " << operand->type;
210 return LayerInputHandle();
211 }
212
213 try
214 {
215 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
216
217 if (IsDynamicTensor(operandTensorInfo))
218 {
219 data.m_DynamicInputsEncountered = true;
220
221 const uint32_t operandIndex = operation.inputs[inputIndex];
222
223 // Check if the dynamic input tensors have been inferred by one of the previous layers
224 // If not we can't support them
225 if (data.m_OutputSlotForOperand.size() >= operandIndex && data.m_OutputSlotForOperand[operandIndex])
226 {
227 operandTensorInfo = data.m_OutputSlotForOperand[operandIndex]->GetTensorInfo();
228 }
229 else
230 {
231 Fail("%s: Type 2 dynamic input tensors are not supported", __func__);
232 return LayerInputHandle();
233 }
234 }
235
236 switch (operand->lifetime)
237 {
238 case OperandLifeTime::SUBGRAPH_INPUT:
239 {
240 // NOTE: We must check whether we can support the input tensor on at least one
241 // of the provided backends; otherwise we cannot convert the operation
242 bool isInputSupported = false;
243 FORWARD_LAYER_SUPPORT_FUNC(__func__,
244 IsInputSupported,
245 data.m_Backends,
246 isInputSupported,
247 operandTensorInfo);
248
249 if (!isInputSupported)
250 {
251 Fail("%s: unsupported input tensor", __func__);
252 return LayerInputHandle();
253 }
254
255 [[clang::fallthrough]]; // intentional fallthrough
256 }
257 case OperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
258 case OperandLifeTime::SUBGRAPH_OUTPUT:
259 {
260 // The tensor is either an operand internal to the model, or a model input.
261 // It can be associated with an ArmNN output slot for an existing layer.
262
263 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
264 const uint32_t operandIndex = operation.inputs[inputIndex];
265 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
266 }
267 case OperandLifeTime::CONSTANT_COPY: // intentional fallthrough
268 case OperandLifeTime::POINTER:
269 case OperandLifeTime::CONSTANT_REFERENCE:
270 {
271 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
272 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin(*operand, model, data, dimensionMappings);
273 if (tensorPin.IsValid())
274 {
275 bool isSupported = false;
276 FORWARD_LAYER_SUPPORT_FUNC(__func__,
277 IsConstantSupported,
278 data.m_Backends,
279 isSupported,
280 tensorPin.GetConstTensor().GetInfo());
281 if (!isSupported)
282 {
283 return LayerInputHandle();
284 }
285
286 armnn::IConnectableLayer* constantLayer =
287 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
288 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
289 outputSlot.SetTensorInfo(tensorPin.GetConstTensor().GetInfo());
290
291 return LayerInputHandle(true, &outputSlot, operandTensorInfo);
292 }
293 else
294 {
295 Fail("%s: invalid operand tensor", __func__);
296 return LayerInputHandle();
297 }
298 break;
299 }
300 default:
301 {
302 VLOG(DRIVER) << __func__ << ": unsupported lifetime for input tensor: " << operand->lifetime;
303 return LayerInputHandle();
304 }
305 }
306 }
307 catch (UnsupportedOperand<OperandType>& e)
308 {
309 VLOG(DRIVER) << __func__ << ": Operand type: " << e.m_type << " not supported in ArmnnDriver";
310 return LayerInputHandle();
311 }
312}
313
314bool ConvertPaddings(const Operation& operation,
315 const Model& model,
316 ConversionData& data,
317 unsigned int rank,
318 armnn::PadDescriptor& padDescriptor)
319{
320 const Operand* paddingsOperand = GetInputOperand(operation, 1, model);
321 if (!paddingsOperand)
322 {
323 return Fail("%s: Could not read paddings operand", __func__);
324 }
325
326 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
327 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
328 {
329 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
330 }
331
332 std::vector<int32_t> paddings;
333 if (!GetTensorInt32Values(*paddingsOperand, paddings, model, data))
334 {
335 return Fail("%s: Operation has invalid or unsupported paddings operand", __func__);
336 }
337
338 // add padding for each dimension of input tensor.
339 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
340 {
341 int paddingBeforeInput = paddings[i];
342 int paddingAfterInput = paddings[i + 1];
343
344 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
345 {
346 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
347 }
348
349 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
350 }
351
352 return true;
353}
354
355
356bool ConvertPooling2d(const Operation& operation,
357 const char* operationName,
358 armnn::PoolingAlgorithm poolType,
359 const Model& model,
360 ConversionData& data)
361{
362
363 VLOG(DRIVER) << "Converter::ConvertL2Pool2d()";
364
365 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
366 if (!input.IsValid())
367 {
368 return Fail("%s: Operation Could not read input 0", operationName);
369 }
370
371 const Operand* output = GetOutputOperand(operation, 0, model);
372 if (!output)
373 {
374 return Fail("%s: Could not read output 0", __func__);
375 }
376
377 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
378 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
379
380 armnn::Pooling2dDescriptor desc;
381 desc.m_PoolType = poolType;
382 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
383 desc.m_DataLayout = armnn::DataLayout::NHWC;
384
385 ActivationFn activation;
386
387 auto inputSize = operation.inputs.size();
388
389 if (inputSize >= 10)
390 {
391 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
392 if (!GetInputScalar(operation, 1, OperandType::INT32, desc.m_PadLeft, model, data) ||
393 !GetInputScalar(operation, 2, OperandType::INT32, desc.m_PadRight, model, data) ||
394 !GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadTop, model, data) ||
395 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadBottom, model, data) ||
396 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideX, model, data) ||
397 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_StrideY, model, data) ||
398 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_PoolWidth, model, data) ||
399 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_PoolHeight, model, data) ||
400 !GetInputActivationFunction(operation, 9, activation, model, data))
401 {
402 return Fail("%s: Operation has invalid inputs", operationName);
403 }
404
405 if (Is12OrLaterOperand(*output))
406 {
407 desc.m_DataLayout = OptionalDataLayout(operation, 10, model, data);
408 }
409 }
410 else
411 {
412 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
413 ::android::nn::PaddingScheme scheme;
414 if (!GetInputPaddingScheme(operation, 1, scheme, model, data) ||
415 !GetInputScalar(operation, 2, OperandType::INT32, desc.m_StrideX, model, data) ||
416 !GetInputScalar(operation, 3, OperandType::INT32, desc.m_StrideY, model, data) ||
417 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PoolWidth, model, data) ||
418 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PoolHeight, model, data) ||
419 !GetInputActivationFunction(operation, 6, activation, model, data))
420 {
421 return Fail("%s: Operation has invalid inputs", operationName);
422 }
423
424 if (Is12OrLaterOperand(*output))
425 {
426 desc.m_DataLayout = OptionalDataLayout(operation, 7, model, data);
427 }
428
429 const armnnUtils::DataLayoutIndexed dataLayout(desc.m_DataLayout);
430 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
431 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
432
433 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
434 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
435 }
436
437 bool isSupported = false;
438
439 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
440 {
441 FORWARD_LAYER_SUPPORT_FUNC(__func__,
442 IsPooling2dSupported,
443 data.m_Backends,
444 isSupported,
445 inputInfo,
446 outputInfo,
447 desc);
448
449 };
450
451 if(IsDynamicTensor(outputInfo))
452 {
453 isSupported = AreDynamicTensorsSupported();
454 }
455 else
456 {
457 validateFunc(outputInfo, isSupported);
458 }
459
460 if (!isSupported)
461 {
462 return false;
463 }
464
465 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
466 if (!pooling2dLayer)
467 {
468 return Fail("%s: AddPooling2dLayer failed", __func__);
469 }
470
471 input.Connect(pooling2dLayer->GetInputSlot(0));
472
473 if (!isSupported)
474 {
475 return false;
476 }
477
478 return SetupAndTrackLayerOutputSlot(operation, 0, *pooling2dLayer, model,
479 data, nullptr, validateFunc, activation);
480}
481
482bool ConvertReduce(const Operation& operation,
483 const Model& model,
484 ConversionData& data,
485 armnn::ReduceOperation reduceOperation)
486{
487 armnn::ReduceDescriptor descriptor;
488 descriptor.m_ReduceOperation = reduceOperation;
489
490 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
491 if (!input.IsValid())
492 {
493 return Fail("%s: Operation has invalid inputs", __func__);
494 }
495 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
496
497 const Operand* output = GetOutputOperand(operation, 0, model);
498 if (!output)
499 {
500 return Fail("%s: Could not read output 0", __func__);
501 }
502 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
503
504 const Operand* axisOperand = GetInputOperand(operation, 1, model);
505 if (!axisOperand)
506 {
507 return Fail("%s: Could not read input 1", __func__);
508 }
509 std::vector<int32_t> axis;
510 if (!GetTensorInt32Values(*axisOperand, axis, model, data))
511 {
512 return Fail("%s: Input 1 has invalid values", __func__);
513 }
514
515 // Convert the axis to unsigned int and remove duplicates.
516 unsigned int rank = inputInfo.GetNumDimensions();
517 std::set<unsigned int> uniqueAxis;
518 std::transform(axis.begin(), axis.end(),
519 std::inserter(uniqueAxis, uniqueAxis.begin()),
520 [rank](int i) -> unsigned int { return (i + rank) % rank; });
521 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
522
523 // Get the "keep dims" flag.
524 if (!GetInputScalar(operation, 2, OperandType::BOOL, descriptor.m_KeepDims, model, data))
525 {
526 return Fail("%s: Could not read input 2", __func__);
527 }
528
529 bool isSupported = false;
530 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
531 {
532 FORWARD_LAYER_SUPPORT_FUNC(__func__,
533 IsReduceSupported,
534 data.m_Backends,
535 isSupported,
536 inputInfo,
537 outputInfo,
538 descriptor);
539 };
540
541 if(!IsDynamicTensor(outputInfo))
542 {
543 validateFunc(outputInfo, isSupported);
544 }
545 else
546 {
547 isSupported = AreDynamicTensorsSupported();
548 }
549
550 if (!isSupported)
551 {
552 return false;
553 }
554
555 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
556 assert(layer != nullptr);
557 input.Connect(layer->GetInputSlot(0));
558
559 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data, nullptr, validateFunc);
560}
561
562
563bool ConvertToActivation(const Operation& operation,
564 const char* operationName,
565 const armnn::ActivationDescriptor& activationDesc,
566 const Model& model,
567 ConversionData& data)
568{
569 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
570 if (!input.IsValid())
571 {
572 return Fail("%s: Input 0 is invalid", operationName);
573 }
574
575 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
576 if (!outputOperand)
577 {
578 return false;
579 }
580
581 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
582
583 bool isSupported = false;
584
585 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
586 {
587 FORWARD_LAYER_SUPPORT_FUNC(__func__,
588 IsActivationSupported,
589 data.m_Backends,
590 isSupported,
591 input.GetTensorInfo(),
592 outInfo,
593 activationDesc);
594 };
595
596 if(IsDynamicTensor(outInfo))
597 {
598 isSupported = AreDynamicTensorsSupported();
599 }
600 else
601 {
602 validateFunc(outInfo, isSupported);
603 }
604
605 if (!isSupported)
606 {
607 return false;
608 }
609
610 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
611 ARMNN_ASSERT(layer != nullptr);
612 input.Connect(layer->GetInputSlot(0));
613
614 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data, nullptr, validateFunc);
615}
616
617DequantizeResult DequantizeIfRequired(size_t operand_index,
618 const Operation& operation,
619 const Model& model,
620 const ConversionData& data)
621{
622 const Operand* weightsOperand = GetInputOperand(operation, operand_index, model);
623 if (!weightsOperand)
624 {
625 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::INVALID_OPERAND };
626 }
627
628 if (IsOperandConstant(*weightsOperand))
629 {
630 // Weights are already constant
631 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::NOT_REQUIRED };
632 }
633
634 const size_t weightsInputIndex = operation.inputs[operand_index];
635
636 // The weights are a non const tensor, this indicates they might be the output of a dequantize op.
637 // Iterate over the nodes and find the previous operation which should be DEQUANTIZE
638 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
639 {
640 // Search for the DEQUANTIZE op which has the operand with index equal to operandIndex
641 const auto& operationIt = getMainModel(model).operations[operationIdx];
642 if (operationIt.type != OperationType::DEQUANTIZE)
643 {
644 continue;
645 }
646
647 size_t outOpIndex = weightsInputIndex + 1;
648 for (size_t i = 0; outOpIndex != weightsInputIndex && i < operationIt.outputs.size(); ++i)
649 {
650 outOpIndex = operationIt.outputs[i];
651 }
652
653 if (outOpIndex != weightsInputIndex)
654 {
655 continue;
656 }
657
658 const Operand* operand = GetInputOperand(operationIt, 0, model);
659 ARMNN_ASSERT(operand);
660
661 if (!IsQSymm8(*operand))
662 {
663 // Only supporting dequantize from QSYMM8 to FLOAT
664 break;
665 }
666
667 // Allocate a new buffer for the dequantized data and manually dequantize
668 const void* startValue = GetOperandValueReadOnlyAddress(*operand, model, data);
669 if (!startValue)
670 {
671 // Failed to get the operand address
672 break;
673 }
674
675 const uint8_t* quantizedBuffer = reinterpret_cast<const uint8_t*>(startValue);
676 size_t dequantizedBufferLength = operand->location.length;
677 const float quantizationScale = operand->scale;
678
679 auto dequantizedBuffer = std::make_unique<float[]>(dequantizedBufferLength + 1);
680 for (size_t i = 0; i < dequantizedBufferLength; ++i)
681 {
682 float* dstPtr = dequantizedBuffer.get();
683 ARMNN_ASSERT(dstPtr);
684 *dstPtr++ = quantizedBuffer[i] * quantizationScale;
685 }
686
687 // Construct tensor info for dequantized ConstTensor
688 armnn::TensorInfo tensorInfo(operand->dimensions.size(),
689 operand->dimensions.data(),
690 armnn::DataType::Float32);
691
692 return { std::move(dequantizedBuffer), dequantizedBufferLength * sizeof(float),
693 std::move(tensorInfo),
694 DequantizeStatus::SUCCESS };
695 }
696
697 return { nullptr, 0, armnn::TensorInfo() , DequantizeStatus::NOT_REQUIRED};
698}
699
700ConstTensorPin DequantizeAndMakeConstTensorPin(const Operation& operation,
701 const Model& model,
702 const ConversionData& data,
703 size_t operandIndex,
704 bool optional)
705{
706 DequantizeResult dequantized = DequantizeIfRequired(operandIndex,operation, model, data);
707
708 DequantizeStatus status = std::get<3>(dequantized);
709 switch (status)
710 {
711 case DequantizeStatus::INVALID_OPERAND:
712 {
713 // return invalid const tensor pin
714 return ConstTensorPin();
715 }
716 case DequantizeStatus::NOT_REQUIRED:
717 {
718 return ConvertOperationInputToConstTensorPin(
719 operation, operandIndex, model, data, g_DontPermute, nullptr, optional);
720 }
721 case DequantizeStatus::SUCCESS:
722 default:
723 {
724 return ConstTensorPin(
725 std::get<2>(dequantized), std::get<0>(dequantized).get(), std::get<1>(dequantized), g_DontPermute);
726 }
727 }
728}
729
730bool GetInputPaddingScheme(const Operation& operation,
731 uint32_t inputIndex,
732 PaddingScheme& outPaddingScheme,
733 const Model& model,
734 const ConversionData& data)
735{
736 int32_t paddingSchemeAsInt;
737 if (!GetInputInt32(operation, inputIndex, paddingSchemeAsInt, model, data))
738 {
739 return Fail("%s: failed to get padding scheme input value", __func__);
740 }
741
742 outPaddingScheme = static_cast<::android::nn::PaddingScheme>(paddingSchemeAsInt);
743 return true;
744}
745
746const void* GetOperandValueReadOnlyAddress(const Operand& operand,
747 const Model& model,
748 const ConversionData& data,
749 bool optional)
750{
751 const void* valueStart = nullptr;
752 switch (operand.lifetime)
753 {
754 case OperandLifeTime::CONSTANT_COPY:
755 {
756 valueStart = model.operandValues.data() + operand.location.offset;
757 break;
758 }
759 case OperandLifeTime::POINTER:
760 {
761 // Pointer specified in the model
762 valueStart = std::get<const void*>(operand.location.pointer);
763 break;
764 }
765 case OperandLifeTime::CONSTANT_REFERENCE:
766 {
767 // Constant specified via a Memory object
768 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
769 break;
770 }
771 case OperandLifeTime::NO_VALUE:
772 {
773 // An optional input tensor with no values is not an error so should not register as a fail
774 if (optional)
775 {
776 valueStart = nullptr;
777 break;
778 }
779 [[fallthrough]];
780 }
781 default:
782 {
783 VLOG(DRIVER) << __func__ << ": unsupported/invalid operand lifetime:: " << operand.lifetime;
784 valueStart = nullptr;
785 }
786 }
787
788 return valueStart;
789}
790
791bool GetTensorInt32Values(const Operand& operand,
792 std::vector<int32_t>& outValues,
793 const Model& model,
794 const ConversionData& data)
795{
796 if (operand.type != OperandType::TENSOR_INT32)
797 {
798 VLOG(DRIVER) << __func__ << ": invalid operand type: " << operand.type;
799 return false;
800 }
801
802 const void* startAddress = GetOperandValueReadOnlyAddress(operand, model, data);
803 if (!startAddress)
804 {
805 VLOG(DRIVER) << __func__ << ": failed to get operand address " << operand.type;
806 return false;
807 }
808
809 // Check number of bytes is sensible
810 const uint32_t numBytes = operand.location.length;
811 if (numBytes % sizeof(int32_t) != 0)
812 {
813 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
814 __func__, numBytes, sizeof(int32_t));
815 }
816
817 outValues.resize(numBytes / sizeof(int32_t));
818 memcpy(outValues.data(), startAddress, numBytes);
819 return true;
820}
821
822armnn::DataLayout OptionalDataLayout(const Operation& operation,
823 uint32_t inputIndex,
824 const Model& model,
825 ConversionData& data)
826{
827 const Operand* operand = GetInputOperand(operation, inputIndex, model);
828 if (!operand)
829 {
830 return armnn::DataLayout::NHWC;
831 }
832
833 if (!IsBool(*operand))
834 {
835 return armnn::DataLayout::NHWC;
836 }
837
838 const void* valueAddress = GetOperandValueReadOnlyAddress(*operand, model, data);
839 if (!valueAddress)
840 {
841 return armnn::DataLayout::NHWC;
842 }
843
844 if (*(static_cast<const bool*>(valueAddress)))
845 {
846 return armnn::DataLayout::NCHW;
847 }
848 else
849 {
850 return armnn::DataLayout::NHWC;
851 }
852}
853
854armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
855 ActivationFn activation,
856 armnn::IConnectableLayer* prevLayer,
857 ConversionData& data)
858{
859 ARMNN_ASSERT(prevLayer->GetNumOutputSlots() == 1);
860
861 prevLayer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
862
863 armnn::IConnectableLayer* activationLayer = prevLayer;
864
865 if (activation != ActivationFn::kActivationNone)
866 {
867 armnn::ActivationDescriptor activationDesc;
868 switch (activation)
869 {
870 case ActivationFn::kActivationRelu:
871 {
872 activationDesc.m_Function = armnn::ActivationFunction::ReLu;
873 break;
874 }
875 case ActivationFn::kActivationRelu1:
876 {
877 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
878 activationDesc.m_A = 1.0f;
879 activationDesc.m_B = -1.0f;
880 break;
881 }
882 case ActivationFn::kActivationRelu6:
883 {
884 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
885 activationDesc.m_A = 6.0f;
886 break;
887 }
888 case ActivationFn::kActivationSigmoid:
889 {
890 activationDesc.m_Function = armnn::ActivationFunction::Sigmoid;
891 break;
892 }
893 case ActivationFn::kActivationTanh:
894 {
895 activationDesc.m_Function = armnn::ActivationFunction::TanH;
896 activationDesc.m_A = 1.0f;
897 activationDesc.m_B = 1.0f;
898 break;
899 }
900 default:
901 {
902 Fail("%s: Invalid activation enum value %i", __func__, activation);
903 return nullptr;
904 }
905 }
906
907 bool isSupported = false;
908 FORWARD_LAYER_SUPPORT_FUNC(__func__,
909 IsActivationSupported,
910 data.m_Backends,
911 isSupported,
912 prevLayer->GetOutputSlot(0).GetTensorInfo(),
913 tensorInfo,
914 activationDesc);
915 if (!isSupported)
916 {
917 return nullptr;
918 }
919
920 activationLayer = data.m_Network->AddActivationLayer(activationDesc);
921
922 prevLayer->GetOutputSlot(0).Connect(activationLayer->GetInputSlot(0));
923 activationLayer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
924 }
925
926 return activationLayer;
927}
928
929bool SetupAndTrackLayerOutputSlot(const Operation& operation,
930 uint32_t operationOutputIndex,
931 armnn::IConnectableLayer& layer,
932 uint32_t layerOutputIndex,
933 const Model& model,
934 ConversionData& data,
935 const armnn::TensorInfo* overrideOutputInfo,
936 const std::function <void (const armnn::TensorInfo&, bool&)>& validateFunc,
937 const ActivationFn& activationFunction,
938 bool inferOutputShapes)
939{
940 const Operand* outputOperand = GetOutputOperand(operation, operationOutputIndex, model);
941 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
942 {
943 return false;
944 }
945
946 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
947 if (overrideOutputInfo == nullptr)
948 {
949 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
950 }
951 else
952 {
953 outputSlot.SetTensorInfo(*overrideOutputInfo);
954 }
955
956 bool isSupported = false;
957 if (validateFunc && (IsDynamicTensor(outputSlot.GetTensorInfo()) || inferOutputShapes))
958 {
959 // Type one dynamic tensors require the previous layer's output shape for inference
960 for (unsigned int inputSlotIndex = 0; inputSlotIndex < layer.GetNumInputSlots(); ++inputSlotIndex)
961 {
962 if(!layer.GetInputSlot(inputSlotIndex).GetConnection())
963 {
964 return false;
965 }
966 }
967 // IsTensorInfoSet will infer the dynamic output shape
968 outputSlot.IsTensorInfoSet();
969 // Once the shape is inferred we can validate it
970 validateFunc(outputSlot.GetTensorInfo(), isSupported);
971
972 if(!isSupported)
973 {
974 for (unsigned int inputSlotIndex = 0; inputSlotIndex < layer.GetNumInputSlots(); ++inputSlotIndex)
975 {
976 layer.GetInputSlot(inputSlotIndex).GetConnection()->Disconnect(layer.GetInputSlot(inputSlotIndex));
977 }
978 return false;
979 }
980 }
981
982 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
983
984 if (activationFunction != ActivationFn::kActivationNone)
985 {
986 const armnn::TensorInfo& activationOutputInfo = outputSlot.GetTensorInfo();
987 armnn::IConnectableLayer* const endLayer = ProcessActivation(activationOutputInfo, activationFunction,
988 &layer, data);
989
990 if (!endLayer)
991 {
992 return Fail("%s: ProcessActivation failed", __func__);
993 }
994
995 armnn::IOutputSlot& activationOutputSlot = endLayer->GetOutputSlot(layerOutputIndex);
996 data.m_OutputSlotForOperand[operandIndex] = &activationOutputSlot;
997 }
998 else
999 {
1000 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1001 }
1002
1003 return true;
1004}
1005
1006} // namespace armnn_driver