blob: 6c5704d7ab9ab2c4e467ff5dbfdc7240aa7dfea5 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5
telsoa014fcda012018-03-09 14:13:49 +00006#include "ClLayerSupport.hpp"
David Beck3e9e1152018-10-17 14:17:50 +01007#include "ClBackendId.hpp"
arovir017c22c702018-10-09 11:16:46 +01008
David Beck3cc9a622018-10-12 10:38:31 +01009#include <armnn/Descriptors.hpp>
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +010010#include <armnn/InternalTypes.hpp>
11#include <armnn/LayerSupportCommon.hpp>
telsoa014fcda012018-03-09 14:13:49 +000012
David Beck3e9e1152018-10-17 14:17:50 +010013#include <backends/LayerSupportRegistry.hpp>
14
telsoa014fcda012018-03-09 14:13:49 +000015#include <boost/core/ignore_unused.hpp>
16
17#ifdef ARMCOMPUTECL_ENABLED
David Beckac42efd2018-09-26 17:41:13 +010018#include "workloads/ClAdditionWorkload.hpp"
Nattapat Chaimanowonge06757e2018-10-11 15:39:18 +010019#include "workloads/ClActivationWorkload.hpp"
David Beckac42efd2018-09-26 17:41:13 +010020#include "workloads/ClBatchNormalizationFloatWorkload.hpp"
21#include "workloads/ClConvertFp16ToFp32Workload.hpp"
22#include "workloads/ClConvertFp32ToFp16Workload.hpp"
Matthew Benthamd8067922018-10-03 17:18:04 +010023#include "workloads/ClConvolution2dWorkload.hpp"
Matthew Benthamd8777392018-10-08 09:38:55 +010024#include "workloads/ClDepthwiseConvolutionWorkload.hpp"
David Beckac42efd2018-09-26 17:41:13 +010025#include "workloads/ClDivisionFloatWorkload.hpp"
David Beckac42efd2018-09-26 17:41:13 +010026#include "workloads/ClFullyConnectedWorkload.hpp"
arovir01085f0a42018-10-08 14:48:19 +010027#include "workloads/ClL2NormalizationFloatWorkload.hpp"
28#include "workloads/ClLstmFloatWorkload.hpp"
Matteo Martincigh28dcab62018-10-19 16:40:03 +010029#include "workloads/ClMeanWorkload.hpp"
arovir01085f0a42018-10-08 14:48:19 +010030#include "workloads/ClMultiplicationWorkload.hpp"
David Beckac42efd2018-09-26 17:41:13 +010031#include "workloads/ClNormalizationFloatWorkload.hpp"
arovir01085f0a42018-10-08 14:48:19 +010032#include "workloads/ClPadWorkload.hpp"
33#include "workloads/ClPermuteWorkload.hpp"
Nattapat Chaimanowongac9e0962018-10-10 17:18:35 +010034#include "workloads/ClPooling2dWorkload.hpp"
David Beckac42efd2018-09-26 17:41:13 +010035#include "workloads/ClSoftmaxBaseWorkload.hpp"
36#include "workloads/ClSubtractionWorkload.hpp"
telsoa014fcda012018-03-09 14:13:49 +000037#endif
38
39using namespace boost;
40
41namespace armnn
42{
arovir017c22c702018-10-09 11:16:46 +010043
telsoa014fcda012018-03-09 14:13:49 +000044namespace
45{
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +010046
David Beck3e9e1152018-10-17 14:17:50 +010047ILayerSupportSharedPtr GetLayerSupportPointer()
48{
49 static ILayerSupportSharedPtr instance{new ClLayerSupport};
50 return instance;
51}
52
53static StaticRegistryInitializer<LayerSupportRegistry> g_RegisterHelper{
54 LayerSupportRegistryInstance(),
55 ClBackendId(),
56 []()
57 {
58 return GetLayerSupportPointer();
59 }
60};
61
telsoa014fcda012018-03-09 14:13:49 +000062template<unsigned int FilterSize>
63bool IsMatchingSize2d(const TensorInfo& weightInfo)
64{
telsoa01c577f2c2018-08-31 09:22:23 +010065 // Width & Height must match.
telsoa014fcda012018-03-09 14:13:49 +000066 return (weightInfo.GetShape()[3] == FilterSize) && (weightInfo.GetShape()[2] == FilterSize);
67}
68
69template<uint32_t ValidStride>
70bool IsMatchingStride(uint32_t actualStride)
71{
72 return ValidStride == actualStride;
73}
74
75template<uint32_t FirstStride, uint32_t SecondStride, uint32_t... ValidStrides>
76bool IsMatchingStride(uint32_t actualStride)
77{
78 return IsMatchingStride<FirstStride>(actualStride) || IsMatchingStride<SecondStride, ValidStrides...>(actualStride);
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +010079}
telsoa014fcda012018-03-09 14:13:49 +000080
arovir01085f0a42018-10-08 14:48:19 +010081bool IsClBackendSupported(Optional<std::string&> reasonIfUnsupported)
telsoa014fcda012018-03-09 14:13:49 +000082{
83#if ARMCOMPUTECL_ENABLED
84 return true;
85#else
arovir01085f0a42018-10-08 14:48:19 +010086 if (reasonIfUnsupported)
telsoa014fcda012018-03-09 14:13:49 +000087 {
arovir01085f0a42018-10-08 14:48:19 +010088 reasonIfUnsupported.value() = "The armnn library has been built without CL support";
telsoa014fcda012018-03-09 14:13:49 +000089 }
90 return false;
91#endif
92}
93
94#if ARMCOMPUTECL_ENABLED
95#define FORWARD_CL_LAYER_SUPPORT_FUNC(expr) (expr)
96#else
97#define FORWARD_CL_LAYER_SUPPORT_FUNC(expr) IsClBackendSupported(reasonIfUnsupported)
98#endif
99
100#if ARMCOMPUTECL_ENABLED
101template<class FuncType, class... Args>
arovir01085f0a42018-10-08 14:48:19 +0100102inline bool IsWorkloadSupported(FuncType&& func, Optional<std::string&> reasonIfUnsupported, Args&&... args)
telsoa014fcda012018-03-09 14:13:49 +0000103{
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)
107 {
arovir01085f0a42018-10-08 14:48:19 +0100108 reasonIfUnsupported.value() = aclStatus.error_description();
telsoa014fcda012018-03-09 14:13:49 +0000109 }
110 return supported;
111}
112
113#define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
114 return IsWorkloadSupported(func, reasonIfUnsupported, __VA_ARGS__);
115#else
116#define FORWARD_WORKLOAD_VALIDATE_FUNC(func, reasonIfUnsupported, ...) \
117 return IsClBackendSupported(reasonIfUnsupported);
118#endif
119
telsoa01c577f2c2018-08-31 09:22:23 +0100120template<typename FloatFunc, typename Uint8Func, typename ... Params>
arovir01085f0a42018-10-08 14:48:19 +0100121bool IsSupportedForDataTypeCl(Optional<std::string&> reasonIfUnsupported,
telsoa014fcda012018-03-09 14:13:49 +0000122 DataType dataType,
telsoa01c577f2c2018-08-31 09:22:23 +0100123 FloatFunc floatFuncPtr,
telsoa014fcda012018-03-09 14:13:49 +0000124 Uint8Func uint8FuncPtr,
125 Params&&... params)
126{
127 return IsClBackendSupported(reasonIfUnsupported) &&
128 IsSupportedForDataTypeGeneric(reasonIfUnsupported,
129 dataType,
130 floatFuncPtr,
telsoa01c577f2c2018-08-31 09:22:23 +0100131 floatFuncPtr,
telsoa014fcda012018-03-09 14:13:49 +0000132 uint8FuncPtr,
133 std::forward<Params>(params)...);
134}
135
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100136} // anonymous namespace
137
138bool ClLayerSupport::IsActivationSupported(const TensorInfo& input,
139 const TensorInfo& output,
140 const ActivationDescriptor& descriptor,
141 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000142{
telsoa01c577f2c2018-08-31 09:22:23 +0100143 FORWARD_WORKLOAD_VALIDATE_FUNC(ClActivationWorkloadValidate,
144 reasonIfUnsupported,
145 input,
146 output,
147 descriptor);
telsoa014fcda012018-03-09 14:13:49 +0000148}
149
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100150bool ClLayerSupport::IsAdditionSupported(const TensorInfo& input0,
151 const TensorInfo& input1,
152 const TensorInfo& output,
153 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000154{
arovir01085f0a42018-10-08 14:48:19 +0100155 FORWARD_WORKLOAD_VALIDATE_FUNC(ClAdditionValidate,
156 reasonIfUnsupported,
157 input0,
158 input1,
159 output);
telsoa014fcda012018-03-09 14:13:49 +0000160}
161
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100162bool ClLayerSupport::IsBatchNormalizationSupported(const TensorInfo& input,
163 const TensorInfo& output,
164 const TensorInfo& mean,
165 const TensorInfo& var,
166 const TensorInfo& beta,
167 const TensorInfo& gamma,
168 const BatchNormalizationDescriptor& descriptor,
169 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000170{
telsoa01c577f2c2018-08-31 09:22:23 +0100171 FORWARD_WORKLOAD_VALIDATE_FUNC(ClBatchNormalizationValidate,
172 reasonIfUnsupported,
173 input,
174 output,
175 mean,
176 var,
177 beta,
178 gamma,
179 descriptor);
telsoa014fcda012018-03-09 14:13:49 +0000180}
181
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100182bool ClLayerSupport::IsConstantSupported(const TensorInfo& output,
183 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000184{
185 return IsSupportedForDataTypeCl(reasonIfUnsupported,
186 output.GetDataType(),
187 &TrueFunc<>,
188 &FalseFuncU8<>);
189}
190
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100191bool ClLayerSupport::IsConvertFp16ToFp32Supported(const TensorInfo& input,
192 const TensorInfo& output,
193 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000194{
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100195 FORWARD_WORKLOAD_VALIDATE_FUNC(ClConvertFp16ToFp32WorkloadValidate,
196 reasonIfUnsupported,
197 input,
198 output);
telsoa014fcda012018-03-09 14:13:49 +0000199}
200
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100201bool ClLayerSupport::IsConvertFp32ToFp16Supported(const TensorInfo& input,
202 const TensorInfo& output,
203 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000204{
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100205 FORWARD_WORKLOAD_VALIDATE_FUNC(ClConvertFp32ToFp16WorkloadValidate,
206 reasonIfUnsupported,
207 input,
208 output);
telsoa014fcda012018-03-09 14:13:49 +0000209}
210
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100211bool ClLayerSupport::IsConvolution2dSupported(const TensorInfo& input,
212 const TensorInfo& output,
213 const Convolution2dDescriptor& descriptor,
214 const TensorInfo& weights,
215 const Optional<TensorInfo>& biases,
216 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000217{
surmeh013537c2c2018-05-18 16:31:43 +0100218 FORWARD_WORKLOAD_VALIDATE_FUNC(ClConvolution2dWorkloadValidate,
219 reasonIfUnsupported,
220 input,
221 output,
222 descriptor,
223 weights,
224 biases);
telsoa014fcda012018-03-09 14:13:49 +0000225}
226
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100227bool ClLayerSupport::IsDepthwiseConvolutionSupported(const TensorInfo& input,
228 const TensorInfo& output,
229 const DepthwiseConvolution2dDescriptor& descriptor,
230 const TensorInfo& weights,
231 const Optional<TensorInfo>& biases,
232 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000233{
telsoa01c577f2c2018-08-31 09:22:23 +0100234 FORWARD_WORKLOAD_VALIDATE_FUNC(ClDepthwiseConvolutionWorkloadValidate,
235 reasonIfUnsupported,
236 input,
237 output,
238 descriptor,
239 weights,
240 biases);
telsoa014fcda012018-03-09 14:13:49 +0000241}
242
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100243bool ClLayerSupport::IsDivisionSupported(const TensorInfo& input0,
244 const TensorInfo& input1,
245 const TensorInfo& output,
246 Optional<std::string&> reasonIfUnsupported) const
Francis Murtaghe7a86a42018-08-29 12:42:10 +0100247{
248 FORWARD_WORKLOAD_VALIDATE_FUNC(ClDivisionWorkloadValidate,
249 reasonIfUnsupported,
250 input0,
251 input1,
252 output);
253}
254
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100255bool ClLayerSupport::IsFakeQuantizationSupported(const TensorInfo& input,
256 const FakeQuantizationDescriptor& descriptor,
257 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000258{
259 ignore_unused(input);
260 ignore_unused(descriptor);
arovir01085f0a42018-10-08 14:48:19 +0100261 ignore_unused(reasonIfUnsupported);
telsoa014fcda012018-03-09 14:13:49 +0000262 return false;
263}
264
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100265bool ClLayerSupport::IsFloorSupported(const TensorInfo& input,
266 const TensorInfo& output,
267 Optional<std::string&> reasonIfUnsupported) const
telsoa014fcda012018-03-09 14:13:49 +0000268{
269 ignore_unused(output);
telsoa01c577f2c2018-08-31 09:22:23 +0100270 return IsClBackendSupported(reasonIfUnsupported) &&
271 IsSupportedForDataTypeGeneric(reasonIfUnsupported,
272 input.GetDataType(),
273 &FalseFuncF16<>,
274 &TrueFunc<>,
275 &FalseFuncU8<>);
276}
277
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100278bool ClLayerSupport::IsFullyConnectedSupported(const TensorInfo& input,
279 const TensorInfo& output,
280 const TensorInfo& weights,
281 const TensorInfo& biases,
282 const FullyConnectedDescriptor& descriptor,
283 Optional<std::string&> reasonIfUnsupported) const
284{
285 FORWARD_WORKLOAD_VALIDATE_FUNC(ClFullyConnectedWorkloadValidate,
286 reasonIfUnsupported,
287 input,
288 output,
289 weights,
290 biases,
291 descriptor);
292}
293
294bool ClLayerSupport::IsInputSupported(const TensorInfo& input,
295 Optional<std::string&> reasonIfUnsupported) const
296{
297 return IsSupportedForDataTypeCl(reasonIfUnsupported,
298 input.GetDataType(),
299 &TrueFunc<>,
300 &TrueFunc<>);
301}
302
303bool ClLayerSupport::IsL2NormalizationSupported(const TensorInfo& input,
304 const TensorInfo& output,
305 const L2NormalizationDescriptor& descriptor,
306 Optional<std::string&> reasonIfUnsupported) const
307{
308 FORWARD_WORKLOAD_VALIDATE_FUNC(ClL2NormalizationWorkloadValidate,
309 reasonIfUnsupported,
310 input,
311 output,
312 descriptor);
313}
314
315bool ClLayerSupport::IsLstmSupported(const TensorInfo& input,
316 const TensorInfo& outputStateIn,
317 const TensorInfo& cellStateIn,
318 const TensorInfo& scratchBuffer,
319 const TensorInfo& outputStateOut,
320 const TensorInfo& cellStateOut,
321 const TensorInfo& output,
322 const LstmDescriptor& descriptor,
323 const TensorInfo& inputToForgetWeights,
324 const TensorInfo& inputToCellWeights,
325 const TensorInfo& inputToOutputWeights,
326 const TensorInfo& recurrentToForgetWeights,
327 const TensorInfo& recurrentToCellWeights,
328 const TensorInfo& recurrentToOutputWeights,
329 const TensorInfo& forgetGateBias,
330 const TensorInfo& cellBias,
331 const TensorInfo& outputGateBias,
332 const TensorInfo* inputToInputWeights,
333 const TensorInfo* recurrentToInputWeights,
334 const TensorInfo* cellToInputWeights,
335 const TensorInfo* inputGateBias,
336 const TensorInfo* projectionWeights,
337 const TensorInfo* projectionBias,
338 const TensorInfo* cellToForgetWeights,
339 const TensorInfo* cellToOutputWeights,
340 Optional<std::string&> reasonIfUnsupported) const
telsoa01c577f2c2018-08-31 09:22:23 +0100341{
arovir01085f0a42018-10-08 14:48:19 +0100342 FORWARD_WORKLOAD_VALIDATE_FUNC(ClLstmFloatWorkloadValidate,
343 reasonIfUnsupported,
344 input,
345 outputStateIn,
346 cellStateIn,
347 scratchBuffer,
348 outputStateOut,
349 cellStateOut,
350 output,
351 descriptor,
352 inputToForgetWeights,
353 inputToCellWeights,
354 inputToOutputWeights,
355 recurrentToForgetWeights,
356 recurrentToCellWeights,
357 recurrentToOutputWeights,
358 forgetGateBias,
359 cellBias,
360 outputGateBias,
361 inputToInputWeights,
362 recurrentToInputWeights,
363 cellToInputWeights,
364 inputGateBias,
365 projectionWeights,
366 projectionBias,
367 cellToForgetWeights,
368 cellToOutputWeights);
telsoa01c577f2c2018-08-31 09:22:23 +0100369}
370
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100371bool ClLayerSupport::IsMeanSupported(const TensorInfo& input,
372 const TensorInfo& output,
373 const MeanDescriptor& descriptor,
374 Optional<std::string&> reasonIfUnsupported) const
narpra0132b90462018-09-13 11:07:48 +0100375{
Matteo Martincigh28dcab62018-10-19 16:40:03 +0100376 FORWARD_WORKLOAD_VALIDATE_FUNC(ClMeanValidate,
377 reasonIfUnsupported,
378 input,
379 output,
380 descriptor);
narpra0132b90462018-09-13 11:07:48 +0100381}
382
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100383bool ClLayerSupport::IsMergerSupported(const std::vector<const TensorInfo*> inputs,
384 const OriginsDescriptor& descriptor,
385 Optional<std::string&> reasonIfUnsupported) const
386{
387 ignore_unused(descriptor);
388 return IsSupportedForDataTypeCl(reasonIfUnsupported,
389 inputs[0]->GetDataType(),
390 &TrueFunc<>,
391 &FalseFuncU8<>);
392}
393
394bool ClLayerSupport::IsMultiplicationSupported(const TensorInfo& input0,
395 const TensorInfo& input1,
396 const TensorInfo& output,
397 Optional<std::string&> reasonIfUnsupported) const
398{
399 FORWARD_WORKLOAD_VALIDATE_FUNC(ClMultiplicationWorkloadValidate,
400 reasonIfUnsupported,
401 input0,
402 input1,
403 output);
404}
405
406bool ClLayerSupport::IsNormalizationSupported(const TensorInfo& input,
407 const TensorInfo& output,
408 const NormalizationDescriptor& descriptor,
409 Optional<std::string&> reasonIfUnsupported) const
410{
411 FORWARD_WORKLOAD_VALIDATE_FUNC(ClNormalizationWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
412}
413
414bool ClLayerSupport::IsOutputSupported(const TensorInfo& output,
415 Optional<std::string&> reasonIfUnsupported) const
416{
417 return IsSupportedForDataTypeCl(reasonIfUnsupported,
418 output.GetDataType(),
419 &TrueFunc<>,
420 &TrueFunc<>);
421}
422
423bool ClLayerSupport::IsPadSupported(const TensorInfo& input,
424 const TensorInfo& output,
425 const PadDescriptor& descriptor,
426 Optional<std::string&> reasonIfUnsupported) const
arovir01085f0a42018-10-08 14:48:19 +0100427{
428 FORWARD_WORKLOAD_VALIDATE_FUNC(ClPadValidate,
429 reasonIfUnsupported,
430 input,
431 output,
432 descriptor);
433}
434
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100435bool ClLayerSupport::IsPermuteSupported(const TensorInfo& input,
436 const TensorInfo& output,
437 const PermuteDescriptor& descriptor,
438 Optional<std::string&> reasonIfUnsupported) const
439{
440 ignore_unused(input);
441 ignore_unused(output);
442 FORWARD_WORKLOAD_VALIDATE_FUNC(ClPermuteWorkloadValidate, reasonIfUnsupported, descriptor);
telsoa014fcda012018-03-09 14:13:49 +0000443}
Aron Virginas-Tarbcf9f162018-10-15 11:47:37 +0100444
445bool ClLayerSupport::IsPooling2dSupported(const TensorInfo& input,
446 const TensorInfo& output,
447 const Pooling2dDescriptor& descriptor,
448 Optional<std::string&> reasonIfUnsupported) const
449{
450 FORWARD_WORKLOAD_VALIDATE_FUNC(ClPooling2dWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
451}
452
453bool ClLayerSupport::IsReshapeSupported(const TensorInfo& input,
454 Optional<std::string&> reasonIfUnsupported) const
455{
456 ignore_unused(input);
457 ignore_unused(reasonIfUnsupported);
458 return true;
459}
460
461bool ClLayerSupport::IsResizeBilinearSupported(const TensorInfo& input,
462 Optional<std::string&> reasonIfUnsupported) const
463{
464 return IsSupportedForDataTypeCl(reasonIfUnsupported,
465 input.GetDataType(),
466 &TrueFunc<>,
467 &FalseFuncU8<>);
468}
469
470bool ClLayerSupport::IsSoftmaxSupported(const TensorInfo& input,
471 const TensorInfo& output,
472 const SoftmaxDescriptor& descriptor,
473 Optional<std::string&> reasonIfUnsupported) const
474{
475 ignore_unused(descriptor);
476 FORWARD_WORKLOAD_VALIDATE_FUNC(ClSoftmaxWorkloadValidate, reasonIfUnsupported, input, output);
477}
478
479bool ClLayerSupport::IsSplitterSupported(const TensorInfo& input,
480 const ViewsDescriptor& descriptor,
481 Optional<std::string&> reasonIfUnsupported) const
482{
483 ignore_unused(descriptor);
484 return IsSupportedForDataTypeCl(reasonIfUnsupported,
485 input.GetDataType(),
486 &TrueFunc<>,
487 &TrueFunc<>);
488}
489
490bool ClLayerSupport::IsSubtractionSupported(const TensorInfo& input0,
491 const TensorInfo& input1,
492 const TensorInfo& output,
493 Optional<std::string&> reasonIfUnsupported) const
494{
495 FORWARD_WORKLOAD_VALIDATE_FUNC(ClSubtractionValidate,
496 reasonIfUnsupported,
497 input0,
498 input1,
499 output);
500}
501
502} // namespace armnn