2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
6 #include "NeonLayerSupport.hpp"
8 #include "LayerSupportCommon.hpp"
9 #include "InternalTypes.hpp"
11 #include <armnn/Descriptors.hpp>
12 #include <armnn/Types.hpp>
13 #include <armnn/Tensor.hpp>
15 #include <boost/core/ignore_unused.hpp>
17 #ifdef ARMCOMPUTENEON_ENABLED
18 #include "NeonWorkloads/NeonPooling2dBaseWorkload.hpp"
19 #include "NeonWorkloads/NeonPermuteWorkload.hpp"
22 using namespace boost;
26 bool IsNeonActivationUint8Supported(std::string* reasonIfUnsupported, const ActivationDescriptor& parameters)
28 if (parameters.m_Function != ActivationFunction::BoundedReLu)
30 if (reasonIfUnsupported)
32 *reasonIfUnsupported = "Unsupported activation function, only BoundedReLu is supported)";
41 bool IsNeonDirectConvolutionPreferred(const TensorInfo& weightInfo, const Convolution2dDescriptor& desc)
43 // See arm_compute::NEDirectConvolutionLayer documentation for the supported cases,
44 // and complement with NEDirectConvolutionLayerKernel::configure() implementation
46 // Only 1x1 is using direct convolution. Performance results and details are in:
47 // https://jira.arm.com/browse/IVGCVSW-1003
48 // Measurements were taken as of clframework: f105ab972135bcd21304883eff040d7e587099bc
50 const bool dataTypeSupported = (weightInfo.GetDataType() == armnn::DataType::Float32);
53 const bool strideSupported = (desc.m_StrideX == 1 || desc.m_StrideX == 2 || desc.m_StrideX == 3) &&
54 (desc.m_StrideY == 1 || desc.m_StrideY == 2 || desc.m_StrideY == 3);
56 auto paddingLargerThan = [](const Convolution2dDescriptor& desc, unsigned int value)
58 return desc.m_PadLeft > value || desc.m_PadRight > value || desc.m_PadTop > value || desc.m_PadBottom > value;
61 // Supported sizes and padding
62 const bool sizeAndPaddingSupported =
63 // Pad > 0 not supported for 1x1 weights
64 (weightInfo.GetShape()[2] == 1 && weightInfo.GetShape()[3] == 1 && !paddingLargerThan(desc, 0u));
66 const bool preferDirectConvolution = dataTypeSupported &&
68 sizeAndPaddingSupported &&
69 // NEDirectConvolutionLayerKernel doesn't support NULL bias
71 return preferDirectConvolution;
74 bool IsNeonMultiplicationParamsSupported(std::string* reasonIfUnsupported,
75 const TensorInfo& info0,
76 const TensorInfo& info1)
78 if (info0.GetShape() == info1.GetShape())
83 if (reasonIfUnsupported)
85 *reasonIfUnsupported = "Multiplication on Neon does not support implicit broadcast.";
90 bool IsNeonNormalizationDescParamsSupported(std::string* reasonIfUnsupported, const NormalizationDescriptor& parameters)
92 if (parameters.m_NormMethodType != NormalizationAlgorithmMethod::LocalBrightness)
94 if (reasonIfUnsupported)
96 *reasonIfUnsupported = "Unsupported normalisation method type, only LocalBrightness is supported";
100 if (parameters.m_NormSize % 2 == 0)
102 if (reasonIfUnsupported)
104 *reasonIfUnsupported = "Normalization size must be an odd number.";
112 bool IsNeonBackendSupported(std::string* reasonIfUnsupported)
114 #if ARMCOMPUTENEON_ENABLED
117 if (reasonIfUnsupported != nullptr)
119 *reasonIfUnsupported = "The armnn library has been built without NEON support";
125 template<typename Float32Func, typename Uint8Func, typename ... Params>
126 bool IsSupportedForDataTypeNeon(std::string* reasonIfUnsupported,
128 Float32Func floatFuncPtr,
129 Uint8Func uint8FuncPtr,
132 return IsNeonBackendSupported(reasonIfUnsupported) &&
133 IsSupportedForDataTypeGeneric(reasonIfUnsupported,
137 std::forward<Params>(params)...);
140 #if ARMCOMPUTENEON_ENABLED
141 template<class FuncType, class... Args>
142 inline bool IsWorkloadSupported(FuncType& func, std::string* reasonIfUnsupported, Args&&... args)
144 arm_compute::Status aclStatus = func(std::forward<Args>(args)...);
145 const bool supported = (aclStatus.error_code() == arm_compute::ErrorCode::OK);
146 if (!supported && reasonIfUnsupported)
148 *reasonIfUnsupported = aclStatus.error_description();
153 #define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
154 return IsWorkloadSupported(func, reasonIfUnsupported, __VA_ARGS__);
156 #define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
157 return IsNeonBackendSupported(reasonIfUnsupported);
160 bool IsActivationSupportedNeon(const TensorInfo& input,
161 const ActivationDescriptor& descriptor,
162 std::string* reasonIfUnsupported)
164 ignore_unused(descriptor);
165 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
167 &TrueFunc<const ActivationDescriptor&>,
168 &IsNeonActivationUint8Supported,
172 bool IsNeonDepthwiseConvolution2dDescParamsSupported(std::string* reasonIfUnsupported,
173 const DepthwiseConvolution2dDescriptor& parameters,
174 const TensorInfo& weights)
176 ignore_unused(weights);
178 if (parameters.m_StrideX < 1 || parameters.m_StrideX > 3)
180 if (reasonIfUnsupported)
182 *reasonIfUnsupported = "m_StrideX can only be 1, 2 or 3";
187 // weights.GetShape()[0] = channel multiplier
188 if (weights.GetShape()[0] != 1)
190 if (reasonIfUnsupported)
192 *reasonIfUnsupported = "Channel multiplier only supports the value 1 in the NEON backend";
197 if (parameters.m_PadLeft != parameters.m_PadRight || parameters.m_PadTop != parameters.m_PadBottom)
199 if (reasonIfUnsupported)
201 *reasonIfUnsupported = "Asymmetric padding for depthwise convolution currently not supported "
210 bool IsAdditionSupportedNeon(const TensorInfo& input0,
211 const TensorInfo& input1,
212 const TensorInfo& output,
213 std::string* reasonIfUnsupported)
215 ignore_unused(input1);
216 ignore_unused(output);
217 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
218 input0.GetDataType(),
223 bool IsBatchNormalizationSupportedNeon(const TensorInfo& input,
224 const BatchNormalizationDescriptor& descriptor,
225 std::string* reasonIfUnsupported)
227 ignore_unused(descriptor);
228 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
234 bool IsConstantSupportedNeon(const TensorInfo& output,
235 std::string* reasonIfUnsupported)
237 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
238 output.GetDataType(),
243 bool IsConvolution2dSupportedNeon(const TensorInfo& input,
244 const Convolution2dDescriptor& descriptor,
245 const TensorInfo& weights,
246 std::string* reasonIfUnsupported)
248 ignore_unused(descriptor);
249 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
255 bool IsDepthwiseConvolutionSupportedNeon(const TensorInfo& input,
256 const DepthwiseConvolution2dDescriptor& descriptor,
257 const TensorInfo& weights,
258 std::string* reasonIfUnsupported)
260 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
262 &IsNeonDepthwiseConvolution2dDescParamsSupported,
263 &IsNeonDepthwiseConvolution2dDescParamsSupported,
268 bool IsFullyConnectedSupportedNeon(const TensorInfo& input,
269 const FullyConnectedDescriptor& descriptor,
270 std::string* reasonIfUnsupported)
272 ignore_unused(descriptor);
273 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
279 bool IsInputSupportedNeon(const TensorInfo& input,
280 std::string* reasonIfUnsupported)
282 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
288 bool IsL2NormalizationSupportedNeon(const TensorInfo& input,
289 std::string* reasonIfUnsupported)
291 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
297 bool IsMergerSupportedNeon(const std::vector<const TensorInfo*> inputs,
298 const OriginsDescriptor& descriptor,
299 std::string* reasonIfUnsupported)
301 ignore_unused(descriptor);
302 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
303 inputs[0]->GetDataType(),
308 bool IsMultiplicationSupportedNeon(const TensorInfo& input0,
309 const TensorInfo& input1,
310 std::string* reasonIfUnsupported)
312 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
313 input0.GetDataType(),
314 &IsNeonMultiplicationParamsSupported,
315 &FalseFuncU8<const TensorInfo&, const TensorInfo&>,
321 bool IsNormalizationSupportedNeon(const TensorInfo& input,
322 const TensorInfo& output,
323 const NormalizationDescriptor& descriptor,
324 std::string* reasonIfUnsupported)
326 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
328 &IsNeonNormalizationDescParamsSupported,
329 &FalseFuncU8<const NormalizationDescriptor&>,
333 bool IsOutputSupportedNeon(const TensorInfo& output,
334 std::string* reasonIfUnsupported)
336 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
337 output.GetDataType(),
342 bool IsPermuteSupportedNeon(const TensorInfo& input,
343 const TensorInfo& output,
344 const PermuteDescriptor& descriptor,
345 std::string* reasonIfUnsupported)
347 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPermuteWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
350 bool IsPooling2dSupportedNeon(const TensorInfo& input,
351 const TensorInfo& output,
352 const Pooling2dDescriptor& descriptor,
353 std::string* reasonIfUnsupported)
355 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPooling2dWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
358 bool IsResizeBilinearSupportedNeon(const TensorInfo& input,
359 std::string* reasonIfUnsupported)
361 ignore_unused(input);
365 bool IsSoftmaxSupportedNeon(const TensorInfo& input,
366 const SoftmaxDescriptor& descriptor,
367 std::string* reasonIfUnsupported)
369 ignore_unused(descriptor);
370 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
376 bool IsSplitterSupportedNeon(const TensorInfo& input,
377 const ViewsDescriptor& descriptor,
378 std::string* reasonIfUnsupported)
380 ignore_unused(descriptor);
381 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
387 bool IsFakeQuantizationSupportedNeon(const TensorInfo& input,
388 const FakeQuantizationDescriptor& descriptor,
389 std::string* reasonIfUnsupported)
391 ignore_unused(input);
392 ignore_unused(descriptor);
396 bool IsReshapeSupportedNeon(const TensorInfo& input,
397 std::string* reasonIfUnsupported)
399 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
405 bool IsFloorSupportedNeon(const TensorInfo& input,
406 const TensorInfo& output,
407 std::string* reasonIfUnsupported)
409 ignore_unused(output);
410 return IsSupportedForDataTypeNeon(reasonIfUnsupported,