2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include "NeonLayerSupport.hpp"
7 #include "NeonBackendId.hpp"
9 #include <armnn/Descriptors.hpp>
10 #include <armnn/Tensor.hpp>
11 #include <armnn/Types.hpp>
12 #include <armnn/BackendRegistry.hpp>
14 #include <InternalTypes.hpp>
15 #include <LayerSupportCommon.hpp>
17 #include <boost/core/ignore_unused.hpp>
19 #if defined(ARMCOMPUTENEON_ENABLED)
20 #include <aclCommon/ArmComputeUtils.hpp>
21 #include <aclCommon/ArmComputeTensorUtils.hpp>
22 #include "workloads/NeonAbsWorkload.hpp"
23 #include "workloads/NeonAdditionWorkload.hpp"
24 #include "workloads/NeonActivationWorkload.hpp"
25 #include "workloads/NeonArgMinMaxWorkload.hpp"
26 #include "workloads/NeonBatchNormalizationWorkload.hpp"
27 #include "workloads/NeonConvolution2dWorkload.hpp"
28 #include "workloads/NeonDepthToSpaceWorkload.hpp"
29 #include "workloads/NeonDepthwiseConvolutionWorkload.hpp"
30 #include "workloads/NeonDequantizeWorkload.hpp"
31 #include "workloads/NeonDetectionPostProcessWorkload.hpp"
32 #include "workloads/NeonGreaterWorkload.hpp"
33 #include "workloads/NeonInstanceNormalizationWorkload.hpp"
34 #include "workloads/NeonL2NormalizationFloatWorkload.hpp"
35 #include "workloads/NeonLstmFloatWorkload.hpp"
36 #include "workloads/NeonMaximumWorkload.hpp"
37 #include "workloads/NeonMeanWorkload.hpp"
38 #include "workloads/NeonConcatWorkload.hpp"
39 #include "workloads/NeonMinimumWorkload.hpp"
40 #include "workloads/NeonMultiplicationWorkload.hpp"
41 #include "workloads/NeonNormalizationFloatWorkload.hpp"
42 #include "workloads/NeonFullyConnectedWorkload.hpp"
43 #include "workloads/NeonPadWorkload.hpp"
44 #include "workloads/NeonPermuteWorkload.hpp"
45 #include "workloads/NeonPooling2dWorkload.hpp"
46 #include "workloads/NeonPreluWorkload.hpp"
47 #include "workloads/NeonQuantizeWorkload.hpp"
48 #include "workloads/NeonQuantizedLstmWorkload.hpp"
49 #include "workloads/NeonResizeWorkload.hpp"
50 #include "workloads/NeonRsqrtWorkload.hpp"
51 #include "workloads/NeonSliceWorkload.hpp"
52 #include "workloads/NeonSoftmaxBaseWorkload.hpp"
53 #include "workloads/NeonSpaceToBatchNdWorkload.hpp"
54 #include "workloads/NeonSpaceToDepthWorkload.hpp"
55 #include "workloads/NeonSplitterWorkload.hpp"
56 #include "workloads/NeonStackWorkload.hpp"
57 #include "workloads/NeonStridedSliceWorkload.hpp"
58 #include "workloads/NeonSubtractionWorkload.hpp"
59 #include "workloads/NeonTransposeConvolution2dWorkload.hpp"
62 using namespace boost;
70 template< typename ... Args>
71 bool IsNeonBackendSupported(Optional<std::string&> reasonIfUnsupported, Args... args)
73 boost::ignore_unused(reasonIfUnsupported, (args)...);
74 #if defined(ARMCOMPUTENEON_ENABLED)
77 SetValueChecked(reasonIfUnsupported, "The armnn library has been built without NEON support");
82 template<typename FloatFunc, typename Uint8Func, typename ... Params>
83 bool IsSupportedForDataTypeNeon(Optional<std::string&> reasonIfUnsupported,
85 FloatFunc floatFuncPtr,
86 Uint8Func uint8FuncPtr,
89 return IsNeonBackendSupported(reasonIfUnsupported) &&
90 IsSupportedForDataTypeGeneric(reasonIfUnsupported,
97 std::forward<Params>(params)...);
100 #if defined(ARMCOMPUTENEON_ENABLED)
101 template<class FuncType, class... Args>
102 inline bool IsWorkloadSupported(FuncType& func, Optional<std::string&> reasonIfUnsupported, Args&&... args)
104 arm_compute::Status aclStatus = func(std::forward<Args>(args)...);
105 const bool supported = (aclStatus.error_code() == arm_compute::ErrorCode::OK);
106 if (!supported && reasonIfUnsupported)
108 reasonIfUnsupported.value() = aclStatus.error_description();
113 #define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
114 return IsWorkloadSupported(func, reasonIfUnsupported, __VA_ARGS__);
116 #define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
117 return IsNeonBackendSupported(reasonIfUnsupported, __VA_ARGS__);
120 #if defined(ARMCOMPUTENEON_ENABLED)
121 #define IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights) \
122 armcomputetensorutils::IsQuantMultiplierSupported(input, output, weights)
124 #define IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights) true
127 } // anonymous namespace
129 bool NeonLayerSupport::IsAbsSupported(const TensorInfo& input,
130 const TensorInfo& output,
131 Optional<std::string&> reasonIfUnsupported) const
133 ElementwiseUnaryDescriptor descriptor(UnaryOperation::Abs);
134 return IsElementwiseUnarySupported(input, output, descriptor, reasonIfUnsupported);
137 bool NeonLayerSupport::IsActivationSupported(const TensorInfo& input,
138 const TensorInfo& output,
139 const ActivationDescriptor& descriptor,
140 Optional<std::string&> reasonIfUnsupported) const
142 ignore_unused(descriptor);
143 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonActivationWorkloadValidate,
150 bool NeonLayerSupport::IsAdditionSupported(const TensorInfo& input0,
151 const TensorInfo& input1,
152 const TensorInfo& output,
153 Optional<std::string&> reasonIfUnsupported) const
155 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonAdditionWorkloadValidate,
162 bool NeonLayerSupport::IsArgMinMaxSupported(const TensorInfo& input,
163 const TensorInfo& output,
164 const ArgMinMaxDescriptor& descriptor,
165 Optional<std::string&> reasonIfUnsupported) const
167 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonArgMinMaxWorkloadValidate,
174 bool NeonLayerSupport::IsBatchNormalizationSupported(const TensorInfo& input,
175 const TensorInfo& output,
176 const TensorInfo& mean,
177 const TensorInfo& var,
178 const TensorInfo& beta,
179 const TensorInfo& gamma,
180 const BatchNormalizationDescriptor& descriptor,
181 Optional<std::string&> reasonIfUnsupported) const
183 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonBatchNormalizationValidate,
194 bool NeonLayerSupport::IsComparisonSupported(const TensorInfo& input0,
195 const TensorInfo& input1,
196 const TensorInfo& output,
197 const ComparisonDescriptor& descriptor,
198 Optional<std::string&> reasonIfUnsupported) const
200 if (descriptor.m_Operation == ComparisonOperation::Greater)
202 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonGreaterWorkloadValidate,
212 bool NeonLayerSupport::IsConcatSupported(const std::vector<const TensorInfo*> inputs,
213 const TensorInfo& output,
214 const ConcatDescriptor& descriptor,
215 Optional<std::string&> reasonIfUnsupported) const
217 if (descriptor.GetNumDimensions() <= descriptor.GetConcatAxis())
219 SetValueChecked(reasonIfUnsupported, "Neon Concat: Concat axis > Number of dimensions.");
223 unsigned int concatInnerAxis = (descriptor.GetNumDimensions() - descriptor.GetConcatAxis()) - 1;
224 if(concatInnerAxis < 3) // Width, height, or channels
226 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonConcatWorkloadValidate,
232 else if (concatInnerAxis == 3)
234 for (auto& input : inputs)
236 if (input && !output.IsTypeSpaceMatch(*input)) // Cannot use sub-tensors if the types are not same space
238 SetValueChecked(reasonIfUnsupported, "Neon Concat: Types and quantization parameters must match.");
242 return true; // Sub-tensors support concat along batch
244 else // > 4 dimensions not supported.
246 SetValueChecked(reasonIfUnsupported, "Neon Concat: Maximum of 4 dimensions supported.");
251 bool NeonLayerSupport::IsConstantSupported(const TensorInfo& output,
252 Optional<std::string&> reasonIfUnsupported) const
254 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
255 output.GetDataType(),
260 bool NeonLayerSupport::IsConvertFp16ToFp32Supported(const TensorInfo& input,
261 const TensorInfo& output,
262 Optional<std::string&> reasonIfUnsupported) const
264 ignore_unused(input);
265 ignore_unused(output);
266 ignore_unused(reasonIfUnsupported);
270 bool NeonLayerSupport::IsConvertFp32ToFp16Supported(const TensorInfo& input,
271 const TensorInfo& output,
272 Optional<std::string&> reasonIfUnsupported) const
274 ignore_unused(input);
275 ignore_unused(output);
276 ignore_unused(reasonIfUnsupported);
280 bool NeonLayerSupport::IsConvolution2dSupported(const TensorInfo& input,
281 const TensorInfo& output,
282 const Convolution2dDescriptor& descriptor,
283 const TensorInfo& weights,
284 const Optional<TensorInfo>& biases,
285 Optional<std::string&> reasonIfUnsupported) const
287 if (!IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights))
292 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonConvolution2dWorkloadValidate,
301 bool NeonLayerSupport::IsDepthToSpaceSupported(const TensorInfo& input,
302 const TensorInfo& output,
303 const DepthToSpaceDescriptor& descriptor,
304 Optional<std::string&> reasonIfUnsupported) const
306 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonDepthToSpaceWorkloadValidate,
313 bool NeonLayerSupport::IsDepthwiseConvolutionSupported(const TensorInfo& input,
314 const TensorInfo& output,
315 const DepthwiseConvolution2dDescriptor& descriptor,
316 const TensorInfo& weights,
317 const Optional<TensorInfo>& biases,
318 Optional<std::string&> reasonIfUnsupported) const
320 if (!IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights))
325 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonDepthwiseConvolutionWorkloadValidate,
334 bool NeonLayerSupport::IsDequantizeSupported(const TensorInfo& input,
335 const TensorInfo& output,
336 Optional<std::string&> reasonIfUnsupported) const
338 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonDequantizeWorkloadValidate,
344 bool NeonLayerSupport::IsDetectionPostProcessSupported(const TensorInfo& boxEncodings,
345 const TensorInfo& scores,
346 const TensorInfo& anchors,
347 const TensorInfo& detectionBoxes,
348 const TensorInfo& detectionClasses,
349 const TensorInfo& detectionScores,
350 const TensorInfo& numDetections,
351 const DetectionPostProcessDescriptor& descriptor,
352 Optional<std::string&> reasonIfUnsupported) const
354 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonDetectionPostProcessValidate,
367 bool NeonLayerSupport::IsDilatedDepthwiseConvolutionSupported(const TensorInfo& input,
368 const TensorInfo& output,
369 const DepthwiseConvolution2dDescriptor& descriptor,
370 const TensorInfo& weights,
371 const Optional<TensorInfo>& biases,
372 Optional<std::string&> reasonIfUnsupported) const
374 if (!IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights))
379 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonDepthwiseConvolutionWorkloadValidate,
388 bool NeonLayerSupport::IsElementwiseUnarySupported(const TensorInfo& input,
389 const TensorInfo& output,
390 const ElementwiseUnaryDescriptor& descriptor,
391 Optional<std::string&> reasonIfUnsupported) const
393 if (descriptor.m_Operation == UnaryOperation::Abs)
395 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonAbsWorkloadValidate,
400 else if (descriptor.m_Operation == UnaryOperation::Rsqrt)
402 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonRsqrtWorkloadValidate,
411 bool NeonLayerSupport::IsFloorSupported(const TensorInfo& input,
412 const TensorInfo& output,
413 Optional<std::string&> reasonIfUnsupported) const
415 ignore_unused(output);
416 return IsNeonBackendSupported(reasonIfUnsupported) &&
417 IsSupportedForDataTypeGeneric(reasonIfUnsupported,
426 bool NeonLayerSupport::IsFullyConnectedSupported(const TensorInfo& input,
427 const TensorInfo& output,
428 const TensorInfo& weights,
429 const TensorInfo& biases,
430 const FullyConnectedDescriptor& descriptor,
431 Optional<std::string&> reasonIfUnsupported) const
433 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonFullyConnectedWorkloadValidate,
442 bool NeonLayerSupport::IsGreaterSupported(const armnn::TensorInfo& input0,
443 const armnn::TensorInfo& input1,
444 const armnn::TensorInfo& output,
445 armnn::Optional<std::string&> reasonIfUnsupported) const
447 ComparisonDescriptor descriptor(ComparisonOperation::Greater);
448 return IsComparisonSupported(input0, input1, output, descriptor, reasonIfUnsupported);
451 bool NeonLayerSupport::IsInputSupported(const TensorInfo& input,
452 Optional<std::string&> reasonIfUnsupported) const
454 return IsNeonBackendSupported(reasonIfUnsupported, input);
457 bool NeonLayerSupport::IsInstanceNormalizationSupported(const TensorInfo& input,
458 const TensorInfo& output,
459 const InstanceNormalizationDescriptor& descriptor,
460 Optional<std::string&> reasonIfUnsupported) const
462 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonInstanceNormalizationWorkloadValidate,
469 bool NeonLayerSupport::IsL2NormalizationSupported(const TensorInfo& input,
470 const TensorInfo& output,
471 const L2NormalizationDescriptor& descriptor,
472 Optional<std::string&> reasonIfUnsupported) const
474 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonL2NormalizationWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
477 bool NeonLayerSupport::IsLstmSupported(const TensorInfo& input,
478 const TensorInfo& outputStateIn,
479 const TensorInfo& cellStateIn,
480 const TensorInfo& scratchBuffer,
481 const TensorInfo& outputStateOut,
482 const TensorInfo& cellStateOut,
483 const TensorInfo& output,
484 const LstmDescriptor& descriptor,
485 const LstmInputParamsInfo& paramsInfo,
486 Optional<std::string&> reasonIfUnsupported) const
488 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonLstmFloatWorkloadValidate,
501 bool NeonLayerSupport::IsMaximumSupported(const TensorInfo& input0,
502 const TensorInfo& input1,
503 const TensorInfo& output,
504 Optional<std::string&> reasonIfUnsupported) const
506 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonMaximumWorkloadValidate,
513 bool NeonLayerSupport::IsMeanSupported(const TensorInfo& input,
514 const TensorInfo& output,
515 const MeanDescriptor& descriptor,
516 Optional<std::string&> reasonIfUnsupported) const
518 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonMeanWorkloadValidate,
525 bool NeonLayerSupport::IsMergerSupported(const std::vector<const TensorInfo*> inputs,
526 const TensorInfo& output,
527 const MergerDescriptor& descriptor,
528 Optional<std::string&> reasonIfUnsupported) const
530 return IsConcatSupported(inputs, output, descriptor, reasonIfUnsupported);
533 bool NeonLayerSupport::IsMinimumSupported(const TensorInfo& input0,
534 const TensorInfo& input1,
535 const TensorInfo& output,
536 Optional<std::string&> reasonIfUnsupported) const
538 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonMinimumWorkloadValidate,
545 bool NeonLayerSupport::IsMultiplicationSupported(const TensorInfo& input0,
546 const TensorInfo& input1,
547 const TensorInfo& output,
548 Optional<std::string&> reasonIfUnsupported) const
550 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonMultiplicationWorkloadValidate,
557 bool NeonLayerSupport::IsNormalizationSupported(const TensorInfo& input,
558 const TensorInfo& output,
559 const NormalizationDescriptor& descriptor,
560 Optional<std::string&> reasonIfUnsupported) const
562 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonNormalizationWorkloadValidate,
569 bool NeonLayerSupport::IsOutputSupported(const TensorInfo& output,
570 Optional<std::string&> reasonIfUnsupported) const
572 return IsNeonBackendSupported(reasonIfUnsupported, output);
575 bool NeonLayerSupport::IsPadSupported(const TensorInfo& input,
576 const TensorInfo& output,
577 const PadDescriptor& descriptor,
578 Optional<std::string&> reasonIfUnsupported) const
580 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPadWorkloadValidate,
587 bool NeonLayerSupport::IsPermuteSupported(const TensorInfo& input,
588 const TensorInfo& output,
589 const PermuteDescriptor& descriptor,
590 Optional<std::string&> reasonIfUnsupported) const
592 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPermuteWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
595 bool NeonLayerSupport::IsPooling2dSupported(const TensorInfo& input,
596 const TensorInfo& output,
597 const Pooling2dDescriptor& descriptor,
598 Optional<std::string&> reasonIfUnsupported) const
600 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPooling2dWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
603 bool NeonLayerSupport::IsPreluSupported(const armnn::TensorInfo &input,
604 const armnn::TensorInfo &alpha,
605 const armnn::TensorInfo &output,
606 armnn::Optional<std::string &> reasonIfUnsupported) const
608 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonPreluWorkloadValidate, reasonIfUnsupported, input, alpha, output);
611 bool NeonLayerSupport::IsQuantizeSupported(const TensorInfo& input,
612 const TensorInfo& output,
613 Optional<std::string&> reasonIfUnsupported) const
615 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonQuantizeWorkloadValidate,
621 bool NeonLayerSupport::IsQuantizedLstmSupported(const TensorInfo& input,
622 const TensorInfo& cellStateIn,
623 const TensorInfo& outputStateIn,
624 const TensorInfo& cellStateOut,
625 const TensorInfo& outputStateOut,
626 const QuantizedLstmInputParamsInfo& paramsInfo,
627 Optional<std::string&> reasonIfUnsupported) const
629 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonQuantizedLstmWorkloadValidate,
639 bool NeonLayerSupport::IsReshapeSupported(const TensorInfo& input,
640 const ReshapeDescriptor& descriptor,
641 Optional<std::string&> reasonIfUnsupported) const
643 ignore_unused(descriptor);
644 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
650 bool NeonLayerSupport::IsResizeSupported(const TensorInfo& input,
651 const TensorInfo& output,
652 const ResizeDescriptor& descriptor,
653 Optional<std::string&> reasonIfUnsupported) const
655 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonResizeWorkloadValidate,
662 bool NeonLayerSupport::IsResizeBilinearSupported(const TensorInfo& input,
663 const TensorInfo& output,
664 Optional<std::string&> reasonIfUnsupported) const
666 ResizeDescriptor descriptor;
667 descriptor.m_Method = ResizeMethod::Bilinear;
668 descriptor.m_DataLayout = DataLayout::NCHW;
670 const TensorShape& outputShape = output.GetShape();
671 descriptor.m_TargetHeight = outputShape[2];
672 descriptor.m_TargetWidth = outputShape[3];
674 return IsResizeSupported(input, output, descriptor, reasonIfUnsupported);
677 bool NeonLayerSupport::IsRsqrtSupported(const TensorInfo& input,
678 const TensorInfo& output,
679 Optional<std::string&> reasonIfUnsupported) const
681 ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt);
682 return IsElementwiseUnarySupported(input, output, descriptor, reasonIfUnsupported);
685 bool NeonLayerSupport::IsSliceSupported(const TensorInfo& input,
686 const TensorInfo& output,
687 const SliceDescriptor& descriptor,
688 Optional<std::string&> reasonIfUnsupported) const
690 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSliceWorkloadValidate,
697 bool NeonLayerSupport::IsSoftmaxSupported(const TensorInfo& input,
698 const TensorInfo& output,
699 const SoftmaxDescriptor& descriptor,
700 Optional<std::string&> reasonIfUnsupported) const
702 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSoftmaxWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
705 bool NeonLayerSupport::IsSpaceToBatchNdSupported(const TensorInfo& input,
706 const TensorInfo& output,
707 const SpaceToBatchNdDescriptor& descriptor,
708 Optional<std::string&> reasonIfUnsupported) const
710 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSpaceToBatchNdWorkloadValidate,
717 bool NeonLayerSupport::IsSpaceToDepthSupported(const TensorInfo& input,
718 const TensorInfo& output,
719 const SpaceToDepthDescriptor& descriptor,
720 Optional<std::string&> reasonIfUnsupported) const
722 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSpaceToDepthWorkloadValidate,
729 bool NeonLayerSupport::IsSplitterSupported(const TensorInfo& input,
730 const ViewsDescriptor& descriptor,
731 Optional<std::string&> reasonIfUnsupported) const
733 ignore_unused(descriptor);
734 return IsSupportedForDataTypeNeon(reasonIfUnsupported,
740 bool NeonLayerSupport::IsSplitterSupported(const TensorInfo& input,
741 const std::vector<std::reference_wrapper<TensorInfo>>& outputs,
742 const ViewsDescriptor& descriptor,
743 Optional<std::string&> reasonIfUnsupported) const
745 #if defined(ARMCOMPUTENEON_ENABLED)
746 // Split along the last dimension, cannot use sub-tensors
747 // as width and height of the sub-tensors do not match
748 // the width and height of the parent tensor
749 // in case of input with more than 2D.
750 std::set<unsigned int> splitAxis = ComputeSplitAxis(descriptor, input.GetShape());
751 if (descriptor.GetNumDimensions() > 2 && splitAxis.size() == 1 &&
752 *splitAxis.begin() == descriptor.GetNumDimensions() - 1 )
754 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSplitterWorkloadValidate,
761 boost::ignore_unused(descriptor);
762 for (auto output : outputs)
764 if (!input.IsTypeSpaceMatch(output)) // Cannot use sub-tensors if the types are not same space
766 SetValueChecked(reasonIfUnsupported, "Neon Splitter: Types and quantization parameters must match.");
773 bool NeonLayerSupport::IsStackSupported(const std::vector<const TensorInfo*>& inputs,
774 const TensorInfo& output,
775 const StackDescriptor& descriptor,
776 Optional<std::string&> reasonIfUnsupported) const
778 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonStackWorkloadValidate,
785 bool NeonLayerSupport::IsStridedSliceSupported(const TensorInfo& input,
786 const TensorInfo& output,
787 const StridedSliceDescriptor& descriptor,
788 Optional<std::string&> reasonIfUnsupported) const
790 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonStridedSliceWorkloadValidate,
797 bool NeonLayerSupport::IsSubtractionSupported(const TensorInfo& input0,
798 const TensorInfo& input1,
799 const TensorInfo& output,
800 Optional<std::string&> reasonIfUnsupported) const
802 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonSubtractionWorkloadValidate,
809 bool NeonLayerSupport::IsTransposeConvolution2dSupported(const TensorInfo& input,
810 const TensorInfo& output,
811 const TransposeConvolution2dDescriptor& descriptor,
812 const TensorInfo& weights,
813 const Optional<TensorInfo>& biases,
814 Optional<std::string&> reasonIfUnsupported) const
816 if (!IS_QUANT_MULTIPLIER_SUPPORTED(input, output, weights))
821 FORWARD_WORKLOAD_VALIDATE_FUNC(NeonTransposeConvolution2dWorkloadValidate,