diff options
Diffstat (limited to 'delegate/opaque/src')
-rw-r--r-- | delegate/opaque/src/ArgMinMax.hpp | 11 | ||||
-rw-r--r-- | delegate/opaque/src/BroadcastTo.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/Control.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/Convolution.hpp | 22 | ||||
-rw-r--r-- | delegate/opaque/src/ElementwiseBinary.hpp | 24 | ||||
-rw-r--r-- | delegate/opaque/src/Fill.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/FullyConnected.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/Pad.hpp | 83 | ||||
-rw-r--r-- | delegate/opaque/src/Redefine.hpp | 31 | ||||
-rw-r--r-- | delegate/opaque/src/Reduce.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/Shape.hpp | 12 | ||||
-rw-r--r-- | delegate/opaque/src/Split.hpp | 32 | ||||
-rw-r--r-- | delegate/opaque/src/StridedSlice.hpp | 73 | ||||
-rw-r--r-- | delegate/opaque/src/Unpack.hpp | 13 | ||||
-rw-r--r-- | delegate/opaque/src/armnn_delegate.cpp | 4 |
15 files changed, 323 insertions, 42 deletions
diff --git a/delegate/opaque/src/ArgMinMax.hpp b/delegate/opaque/src/ArgMinMax.hpp index 5ea7aa8655..f5b9e66ab2 100644 --- a/delegate/opaque/src/ArgMinMax.hpp +++ b/delegate/opaque/src/ArgMinMax.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -71,6 +71,15 @@ TfLiteStatus VisitArgMinMaxOperator(DelegateData& delegateData, const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + if(outputTensorInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnDelegate: NotSpecified Dimensionality is not supported in operator #%d node #%d: ", + argMinMaxOperatorCode, nodeIndex); + return kTfLiteError; + } + // Get const axis value from model and set it to descriptor. if (!IsValid(tfLiteContext, tfLiteAxisTensor, argMinMaxOperatorCode, nodeIndex)) { diff --git a/delegate/opaque/src/BroadcastTo.hpp b/delegate/opaque/src/BroadcastTo.hpp index 379587546f..8fcea9393c 100644 --- a/delegate/opaque/src/BroadcastTo.hpp +++ b/delegate/opaque/src/BroadcastTo.hpp @@ -1,11 +1,12 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once #include <OpaqueDelegateUtils.hpp> +#include <DelegateUtils.hpp> namespace armnnOpaqueDelegate { @@ -102,6 +103,15 @@ namespace armnnOpaqueDelegate const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + if (ZeroDimPresent({inputTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d: ", + broadcastToOperatorCode, nodeIndex); + return kTfLiteError; + } + auto* shapeData = static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLiteShapeTensor)); int32_t shapeTensorNum = TfLiteOpaqueTensorDim(tfLiteShapeTensor, 0); diff --git a/delegate/opaque/src/Control.hpp b/delegate/opaque/src/Control.hpp index 9aef8380af..b7862ddee5 100644 --- a/delegate/opaque/src/Control.hpp +++ b/delegate/opaque/src/Control.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -230,6 +230,16 @@ TfLiteStatus VisitMeanOperator(DelegateData& delegateData, const armnn::TensorInfo& axisTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteAxisTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + // Check for unsupported 0-size dimensions in the tensor shapes + if(ZeroDimPresent({inputTensorInfo, axisTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d", + tfLiteMeanOperatorCode, nodeIndex); + return kTfLiteError; + } + auto* axisTensorData = static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLiteAxisTensor)); std::vector<int32_t> axis; diff --git a/delegate/opaque/src/Convolution.hpp b/delegate/opaque/src/Convolution.hpp index e4393e7bb0..744108713c 100644 --- a/delegate/opaque/src/Convolution.hpp +++ b/delegate/opaque/src/Convolution.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -742,6 +742,21 @@ TfLiteStatus VisitTransposeConv2dOperator(DelegateData& delegateData, const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); const armnn::TensorInfo& filterTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteFilterTensor); + TfLiteFusedActivation activationType = kTfLiteActNone; + if (parameters->activation != kTfLiteActNone) + { + activationType = parameters->activation; + TfLiteStatus activationStatus = ValidateFusedActivationOperator(delegateData, + tfLiteContext, + outputTensorInfo, + outputTensorInfo, + activationType); + if(activationStatus != kTfLiteOk) + { + return kTfLiteError; + } + } + // TfLite uses NHWC tensors const unsigned int inputHeight = inputTensorInfo.GetShape()[1]; const unsigned int inputWidth = inputTensorInfo.GetShape()[2]; @@ -876,6 +891,11 @@ TfLiteStatus VisitTransposeConv2dOperator(DelegateData& delegateData, return kTfLiteError; } + if (activationType != kTfLiteActNone) + { + return FusedActivation(tfLiteContext, tfLiteNode, activationType, layer, 0, delegateData, nodeIndex); + } + return kTfLiteOk; } diff --git a/delegate/opaque/src/ElementwiseBinary.hpp b/delegate/opaque/src/ElementwiseBinary.hpp index 2a67802028..ab946fa3a2 100644 --- a/delegate/opaque/src/ElementwiseBinary.hpp +++ b/delegate/opaque/src/ElementwiseBinary.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once @@ -247,7 +247,7 @@ std::pair<armnn::IConnectableLayer*, armnn::IConnectableLayer*> AddFloorDivLayer const armnn::TensorInfo& outputTensorInfo, int nodeIndex) { - auto layerName = GetName(armnn::BinaryOperation::Div, nodeIndex); + auto layerName = "FloorDiv:" + std::to_string(nodeIndex); armnn::IConnectableLayer* divisionLayer = delegateData.m_Network->AddElementwiseBinaryLayer( armnn::BinaryOperation::Div, layerName.c_str()); @@ -259,7 +259,7 @@ std::pair<armnn::IConnectableLayer*, armnn::IConnectableLayer*> AddFloorDivLayer } armnn::IOutputSlot& outputSlot = divisionLayer->GetOutputSlot(0); outputSlot.SetTensorInfo(outputTensorInfo); - auto floorName = GetName(armnn::LayerType::Floor, nodeIndex); + auto floorName = GetName(armnn::BinaryOperation::Div, nodeIndex); armnn::IConnectableLayer* floorLayer = delegateData.m_Network->AddFloorLayer(floorName.c_str()); outputSlot.Connect(floorLayer->GetInputSlot(0)); return std::make_pair(divisionLayer, floorLayer); @@ -326,7 +326,25 @@ TfLiteStatus VisitElementwiseBinaryOperator(DelegateData& delegateData, armnn::TensorInfo inputTensorInfo1 = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor1); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + // Check for unspecified dimensions in the output tensor + if (outputTensorInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Shape dimensionality is not specified in operator #%d node #%d: ", + elementwiseBinaryOperatorCode, nodeIndex); + return kTfLiteError; + } + // Check for unsupported 0-size dimensions in the tensor shapes + if(ZeroDimPresent({inputTensorInfo0, inputTensorInfo1, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d", + elementwiseBinaryOperatorCode, nodeIndex); + return kTfLiteError; + } // Check if we need to expand the dims of the input tensor infos. // This is required for a few of the backends. diff --git a/delegate/opaque/src/Fill.hpp b/delegate/opaque/src/Fill.hpp index fe27255590..c5baef987f 100644 --- a/delegate/opaque/src/Fill.hpp +++ b/delegate/opaque/src/Fill.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -48,6 +48,16 @@ namespace armnnOpaqueDelegate const TfLiteOpaqueTensor* tfLiteFillTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]); + + if(TfLiteOpaqueTensorGetAllocationType(tfLiteFillTensor) != kTfLiteMmapRo) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: FILL tensor must be constant - not supported in operator #%d node #%d: ", + tfLiteFillOperatorCode, nodeIndex); + return kTfLiteError; + } + if (!IsValid(tfLiteContext, tfLiteFillTensor, tfLiteFillOperatorCode, nodeIndex)) { return kTfLiteError; diff --git a/delegate/opaque/src/FullyConnected.hpp b/delegate/opaque/src/FullyConnected.hpp index 7be06683a5..b7ada030db 100644 --- a/delegate/opaque/src/FullyConnected.hpp +++ b/delegate/opaque/src/FullyConnected.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -73,6 +73,16 @@ TfLiteStatus VisitFullyConnectedOperator(DelegateData& delegateData, const armnn::TensorInfo& weightsTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteWeightsTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + // Check for zero dimension in input and output tensors + if(ZeroDimPresent({inputTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d: ", + operatorCode, nodeIndex); + return kTfLiteError; + } + // Check that we support fused activation before we attempt to create a layer auto* tfLiteNodeParameters = reinterpret_cast<TfLiteFullyConnectedParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode)); diff --git a/delegate/opaque/src/Pad.hpp b/delegate/opaque/src/Pad.hpp index 4305224003..0b9b305675 100644 --- a/delegate/opaque/src/Pad.hpp +++ b/delegate/opaque/src/Pad.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -16,6 +16,7 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, int nodeIndex, int32_t tfLitePadOperatorCode) { + switch(tfLitePadOperatorCode) { case kTfLiteBuiltinMirrorPad: @@ -32,6 +33,7 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, // Inputs int numInputs = 0; const int* inputTensors; + if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk) { TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( @@ -44,12 +46,18 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]); if (!IsValid(tfLiteContext, tfLiteInputTensor, tfLitePadOperatorCode, nodeIndex)) { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Invalid input tensor at node #%d: ", + nodeIndex); return kTfLiteError; } const TfLiteOpaqueTensor* tfLitePaddingTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[1]); if (!IsValid(tfLiteContext, tfLitePaddingTensor, tfLitePadOperatorCode, nodeIndex)) { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Invalid padding tensor at node #%d: ", + nodeIndex); return kTfLiteError; } @@ -70,6 +78,8 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]); if (!IsValid(tfLiteContext, tfLiteOutputTensor, tfLitePadOperatorCode, nodeIndex)) { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Invalid output tensor at node #%d: ", nodeIndex); return kTfLiteError; } @@ -77,25 +87,57 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, const armnn::TensorInfo& paddingTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLitePaddingTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); - // Get the padding data from the input tensor - auto* paddingData = static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLitePaddingTensor)); + armnn::PadDescriptor descriptor; + // Get the padding size from the input tensor 1 size_t step = 2; - armnn::PadDescriptor descriptor; - for (unsigned int i = 0; i < paddingTensorInfo.GetNumElements() / step; ++i) + if(paddingTensorInfo.GetDataType() == armnn::DataType::Signed64) { - descriptor.m_PadList.emplace_back(paddingData[i * step], paddingData[i * step + 1]); + auto* paddingData = static_cast<int64_t*>(TfLiteOpaqueTensorData(tfLitePaddingTensor)); + for (uint16_t i = 0; i < paddingTensorInfo.GetNumElements() / step; ++i) + { + descriptor.m_PadList.emplace_back(paddingData[i * step], paddingData[i * step + 1]); + } + } + else + { + auto* paddingData = static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLitePaddingTensor)); + for (uint16_t i = 0; i < paddingTensorInfo.GetNumElements() / step; ++i) + { + descriptor.m_PadList.emplace_back(paddingData[i * step], paddingData[i * step + 1]); + } } + // Get the padding value from input tensor 2 if (tfLitePadOperatorCode == kTfLiteBuiltinPad && inputTensorInfo.IsQuantized()) { descriptor.m_PadValue = inputTensorInfo.GetQuantizationOffset(); } else if (tfLitePadOperatorCode == kTfLiteBuiltinPadv2) { - const TfLiteOpaqueTensor* tfLitepaddingValue = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, + const TfLiteOpaqueTensor* tfLitePaddingValue = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[2]); - armnn::TensorInfo paddingValueTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLitepaddingValue); + if (!IsValid(tfLiteContext, tfLitePaddingValue, tfLitePadOperatorCode, nodeIndex)) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Invalid padding value at node #%d: ", + nodeIndex); + return kTfLiteError; + } + + // Fall back to TFLite if the padding value input is passed through a non-constant tensor, + // as the armnn delegate doesn't handle non-constant but non-network tensor input well + if(TfLiteOpaqueTensorGetAllocationType(tfLitePaddingValue) != kTfLiteMmapRo) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Unsupported padding input through non-const tensor " + "in operator #%d node #%d", + tfLitePadOperatorCode, nodeIndex); + return kTfLiteError; + } + + armnn::TensorInfo paddingValueTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLitePaddingValue); if (paddingValueTensorInfo.GetNumElements() != 1) { TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( @@ -104,17 +146,21 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, tfLitePadOperatorCode, nodeIndex); return kTfLiteError; } + // Get the padding value from the input tensor - switch (TfLiteOpaqueTensorType(tfLitepaddingValue)) + switch (TfLiteOpaqueTensorType(tfLitePaddingValue)) { case kTfLiteFloat32: - descriptor.m_PadValue = static_cast<float*>(TfLiteOpaqueTensorData(tfLitepaddingValue))[0]; + descriptor.m_PadValue = *static_cast<float*>(TfLiteOpaqueTensorData(tfLitePaddingValue)); + break; + case kTfLiteInt32: + descriptor.m_PadValue = *static_cast<int32_t*>(TfLiteOpaqueTensorData(tfLitePaddingValue)); break; case kTfLiteUInt8: - descriptor.m_PadValue = static_cast<uint8_t*>(TfLiteOpaqueTensorData(tfLitepaddingValue))[0]; + descriptor.m_PadValue = *static_cast<uint8_t*>(TfLiteOpaqueTensorData(tfLitePaddingValue)); break; case kTfLiteInt8: - descriptor.m_PadValue = static_cast<int8_t*>(TfLiteOpaqueTensorData(tfLitepaddingValue))[0]; + descriptor.m_PadValue = *static_cast<int8_t*>(TfLiteOpaqueTensorData(tfLitePaddingValue)); break; default: TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( @@ -166,9 +212,9 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, } armnn::BackendId setBackend; - if (!delegateData.m_Network) + bool isSupported = false; + auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo) { - bool isSupported = false; FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("PAD", tfLiteContext, IsPadSupported, @@ -178,7 +224,11 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, inputTensorInfo, outputTensorInfo, descriptor); + }; + if (!delegateData.m_Network) + { + validateFunc(outputTensorInfo); return isSupported ? kTfLiteOk : kTfLiteError; } @@ -190,6 +240,11 @@ TfLiteStatus VisitPadOperator(DelegateData& delegateData, armnn::IOutputSlot& outputSlot = padLayer->GetOutputSlot(0); outputSlot.SetTensorInfo(outputTensorInfo); + if (ProcessInputs(padLayer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk) + { + return kTfLiteError; + } + return Connect(padLayer, tfLiteContext, tfLiteNode, delegateData); } diff --git a/delegate/opaque/src/Redefine.hpp b/delegate/opaque/src/Redefine.hpp index 6319ca7841..477449a3bc 100644 --- a/delegate/opaque/src/Redefine.hpp +++ b/delegate/opaque/src/Redefine.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once @@ -147,6 +147,16 @@ TfLiteStatus VisitReshapeOperator(DelegateData& delegateData, const armnn::TensorInfo& inputTensorInfo0 = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + // Check for unsupported 0-size dimensions in the input/output tensor shapes + if(ZeroDimPresent({inputTensorInfo0, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d", + operatorCode, nodeIndex); + return kTfLiteError; + } + armnn::ReshapeDescriptor reshapeDesc; std::vector<int32_t> targetShape; @@ -326,19 +336,22 @@ TfLiteStatus VisitSqueezeOperator(DelegateData& delegateData, std::vector<uint32_t> squeezeDim; // A single negative dim index is interpreted as a negative index in python // Meaning the index will be the shape size plus the negative index value - if (options->num_squeeze_dims == 1 && options->squeeze_dims[0] < 0) - { - int32_t dim = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()) + options->squeeze_dims[0]; - squeezeDim.push_back(static_cast<uint32_t>(dim)); - } - else + + for (int32_t i = 0; i < options->num_squeeze_dims; ++i) { - for (int32_t i = 0; i < options->num_squeeze_dims; ++i) + int32_t dim = options->squeeze_dims[i]; + if(dim < 0) + { + dim += static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()); + squeezeDim.emplace_back(dim); + } + else { - squeezeDim.push_back(static_cast<uint32_t>(options->squeeze_dims[i])); + squeezeDim.emplace_back(static_cast<uint32_t>(options->squeeze_dims[i])); } } + armnn::TensorInfo outputTensorInfo = OutputShapeOfSqueeze(squeezeDim, inputTensorInfo); armnn::ReshapeDescriptor reshapeDesc; diff --git a/delegate/opaque/src/Reduce.hpp b/delegate/opaque/src/Reduce.hpp index a7948ae98d..c13f82b9a6 100644 --- a/delegate/opaque/src/Reduce.hpp +++ b/delegate/opaque/src/Reduce.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #pragma once @@ -70,6 +70,16 @@ TfLiteStatus VisitReduceOperator(DelegateData& delegateData, const armnn::TensorInfo& axisTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteAxisTensor); auto* axisTensorData = static_cast<int*>(TfLiteOpaqueTensorData(tfLiteAxisTensor)); + // Check for unsupported 0-size dimensions in the tensor shapes + if(ZeroDimPresent({inputTensorInfo, axisTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d", + reduceOperatorCode, nodeIndex); + return kTfLiteError; + } + std::vector<int32_t> axis; // Add axis data to vector to be converter to unsigned int and assigned to descriptor axis. if (axisTensorData != nullptr) diff --git a/delegate/opaque/src/Shape.hpp b/delegate/opaque/src/Shape.hpp index 9f15a4f739..e3e4b3aaee 100644 --- a/delegate/opaque/src/Shape.hpp +++ b/delegate/opaque/src/Shape.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -58,6 +58,16 @@ TfLiteStatus VisitShapeOperator(DelegateData& delegateData, const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor); const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + // Check for zero dimension in input and output tensors + if(ZeroDimPresent({inputTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d: ", + operatorCode, nodeIndex); + return kTfLiteError; + } + auto* shapeParameters = reinterpret_cast<TfLiteShapeParams*>(TfLiteOpaqueNodeGetBuiltinData(tfLiteNode)); if (shapeParameters->out_type != kTfLiteInt32 && shapeParameters->out_type != kTfLiteInt64) { diff --git a/delegate/opaque/src/Split.hpp b/delegate/opaque/src/Split.hpp index 199f46b126..0c0c930a14 100644 --- a/delegate/opaque/src/Split.hpp +++ b/delegate/opaque/src/Split.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -100,7 +100,19 @@ TfLiteStatus VisitSplitOperator(DelegateData& delegateData, { return kTfLiteError; } - outputs.push_back(GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true)); + + const armnn::TensorInfo& outputTensorShape = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + + if (ZeroDimPresent({outputTensorShape})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d: ", + tfLiteSplitOperatorCode, nodeIndex); + return kTfLiteError; + } + + outputs.push_back(outputTensorShape); } const std::vector<std::reference_wrapper<armnn::TensorInfo>> outputTensorInfos(outputs.begin(), outputs.end()); @@ -294,16 +306,30 @@ TfLiteStatus VisitSplitVOperator(DelegateData& delegateData, nodeIndex); return kTfLiteError; } + std::vector<armnn::TensorInfo> outputs; for (int i = 0; i < numSplits; ++i) { const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[i]); + if (!IsValid(tfLiteContext, tfLiteOutputTensor, tfLiteSplitVOperatorCode, nodeIndex)) { return kTfLiteError; } - outputs.push_back(GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true)); + + const armnn::TensorInfo& outputTensorShape = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + + if (ZeroDimPresent({outputTensorShape})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d: ", + tfLiteSplitVOperatorCode, nodeIndex); + return kTfLiteError; + } + + outputs.push_back(outputTensorShape); } const std::vector<std::reference_wrapper<armnn::TensorInfo>> outputTensorInfos(outputs.begin(), outputs.end()); diff --git a/delegate/opaque/src/StridedSlice.hpp b/delegate/opaque/src/StridedSlice.hpp index 2e17e3292f..46e5eecbb1 100644 --- a/delegate/opaque/src/StridedSlice.hpp +++ b/delegate/opaque/src/StridedSlice.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -42,12 +42,35 @@ TfLiteStatus VisitStridedSliceOperator(DelegateData& delegateData, { return kTfLiteError; } + // Checking for unsupported non-const non-network input tensors + // Index 0 is the input, index 1-3 should be constant + if(i > 0 && TfLiteOpaqueTensorGetAllocationType(inputTensor) != kTfLiteMmapRo) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Unsupported constant data input through non-const tensor " + "in operator #%d node #%d", + tfLiteStridedSliceOperatorCode, nodeIndex); + return kTfLiteError; + } } const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensors[0]); // We save the begin, end and strides tensors in our descriptor. Therefore we have to read those values from inputs unsigned int inputRank = inputTensorInfo.GetNumDimensions(); + + // Input tensors of rank greater than 4 are unsupported - delegate back to TFLite runtime + if(inputRank > 4) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLitearmnnOpaqueDelegate: Tensors of rank greater than 4 are unsupported" + " in the StridedSlice operator. Operator: #%d node #%d: ", + tfLiteStridedSliceOperatorCode, nodeIndex); + return kTfLiteError; + } + auto ReadInt32Input = [&](int inputIndex, std::vector<int32_t>& outputData) -> TfLiteStatus { if (TfLiteOpaqueTensorType(tfLiteInputTensors[inputIndex]) != kTfLiteInt32) @@ -110,6 +133,44 @@ TfLiteStatus VisitStridedSliceOperator(DelegateData& delegateData, descriptor.m_ShrinkAxisMask = nodeParameters->shrink_axis_mask; descriptor.m_DataLayout = armnn::DataLayout::NHWC; + // Checking begin and end bounds with ShrinkAxisMask + for(unsigned int i = 0; i < inputRank; ++i) + { + if((descriptor.m_ShrinkAxisMask & (1 << i)) && + (((descriptor.m_Begin[i] - descriptor.m_End[i]) > 1) || + ((descriptor.m_Begin[i] - descriptor.m_End[i]) < -1))) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLitearmnnOpaqueDelegate: Invalid combination of ShrinkAxisMask, Begin- and End-Tensor values " + "in the StridedSlice operator. Operator: #%d node #%d: ", + tfLiteStridedSliceOperatorCode, nodeIndex); + return kTfLiteError; + } + } + + // Checking that NewAxisMask doesn't extend the output beyond the supported rank + if(inputRank >= 3 && (descriptor.m_NewAxisMask > 4 || descriptor.m_NewAxisMask == 3)) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLitearmnnOpaqueDelegate: Maximum output tensor rank is 4, the currently set NewAxisMask " + "results in an unsupported higher rank. Operator: #%d node #%d: ", + tfLiteStridedSliceOperatorCode, nodeIndex); + return kTfLiteError; + } + + // The variable 'offset' is documented in TFLite builtin_op_data.h: + // "If true, then the end tensor is an offset of the begin tensor." + if(nodeParameters->offset && + descriptor.m_Begin.size() == descriptor.m_End.size()) + { + for(unsigned int i = 0; i < descriptor.m_End.size(); ++i) + { + descriptor.m_End[i] += descriptor.m_Begin[i]; + } + } + // Validate output // Gather output indices and use to get output tensor. const int* outputTensors; @@ -131,6 +192,16 @@ TfLiteStatus VisitStridedSliceOperator(DelegateData& delegateData, const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor); + // Check for unsupported 0-size dimensions in the input/output tensor shapes + if(ZeroDimPresent({inputTensorInfo, outputTensorInfo})) + { + TF_LITE_OPAQUE_MAYBE_KERNEL_LOG( + tfLiteContext, + "TfLiteArmnnOpaqueDelegate: Zero dimension tensors are not supported in operator #%d node #%d", + tfLiteStridedSliceOperatorCode, nodeIndex); + return kTfLiteError; + } + bool isSupported = false; armnn::BackendId setBackend; auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported) diff --git a/delegate/opaque/src/Unpack.hpp b/delegate/opaque/src/Unpack.hpp index 525529ff7b..29acfdff01 100644 --- a/delegate/opaque/src/Unpack.hpp +++ b/delegate/opaque/src/Unpack.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -131,7 +131,16 @@ TfLiteStatus VisitUnpackOperator(DelegateData& delegateData, return kTfLiteError; } - outputs.push_back(GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true)); + armnn::TensorInfo outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true); + armnn::TensorShape shape = outputTensorInfo.GetShape(); + if (shape.GetDimensionality() == armnn::Dimensionality::NotSpecified) + { + shape.SetNumDimensions(1, true); + shape.SetDimensionSize(0, 1); + outputTensorInfo.SetShape(shape); + } + + outputs.push_back(outputTensorInfo); } const std::vector<std::reference_wrapper<armnn::TensorInfo>> outputTensorInfos(outputs.begin(), outputs.end()); diff --git a/delegate/opaque/src/armnn_delegate.cpp b/delegate/opaque/src/armnn_delegate.cpp index 71ef114b80..f45420d74b 100644 --- a/delegate/opaque/src/armnn_delegate.cpp +++ b/delegate/opaque/src/armnn_delegate.cpp @@ -110,8 +110,8 @@ armnnDelegate::DelegateOptions ParseArmNNSettings(const tflite::TFLiteSettings* // Build the key and value lists to pass into the constructor of the DelegateOptions size_t num_options = options.size(); - std::unique_ptr<const char*> options_keys = std::unique_ptr<const char*>(new const char*[num_options + 1]); - std::unique_ptr<const char*> options_values = std::unique_ptr<const char*>(new const char*[num_options + 1]); + std::unique_ptr<const char*[]> options_keys = std::unique_ptr<const char*[]>(new const char*[num_options + 1]); + std::unique_ptr<const char*[]> options_values = std::unique_ptr<const char*[]>(new const char*[num_options + 1]); for (size_t i=0; i<num_options; ++i) { |