IVGCVSW-5093 Add NEON Logical workload
* Add NEON Logical workloads for NOT,
AND and OR.
* Enable Layer and IsSupported tests on NEON.
Signed-off-by: James Conroy <james.conroy@arm.com>
Change-Id: Ibca59530457a664ca3d77751825642f8daf52fab
diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
index 7c7ad5f..1492a80 100644
--- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
+++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp
@@ -907,6 +907,70 @@
}
template<typename FactoryType, armnn::DataType InputDataType , armnn::DataType OutputDataType>
+bool IsLogicalBinaryLayerSupportedTests(std::string& reasonIfUnsupported)
+{
+ armnn::Graph graph;
+ armnn::LogicalBinaryDescriptor desc(armnn::LogicalBinaryOperation::LogicalOr);
+
+ armnn::Layer* const input0 = graph.AddLayer<armnn::InputLayer>(0, "input0");
+ armnn::Layer* const input1 = graph.AddLayer<armnn::InputLayer>(1, "input1");
+
+ armnn::Layer* const layer = graph.AddLayer<armnn::LogicalBinaryLayer>(desc, "logicalOrLayer");
+
+ armnn::Layer* const output = graph.AddLayer<armnn::OutputLayer>(0, "output1");
+
+ armnn::TensorInfo inputTensorInfo0({1, 1, 1, 4}, InputDataType);
+ armnn::TensorInfo inputTensorInfo1({1, 1, 1, 4}, InputDataType);
+
+ armnn::TensorInfo outputTensorInfo({1, 1, 1, 4}, OutputDataType);
+
+ input0->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
+ input1->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
+
+ input0->GetOutputHandler(0).SetTensorInfo(inputTensorInfo0);
+ input1->GetOutputHandler(0).SetTensorInfo(inputTensorInfo1);
+
+ layer->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+ layer->GetOutputHandler(0).SetTensorInfo(outputTensorInfo);
+
+ bool result = FactoryType::IsLayerSupported(*layer, InputDataType, reasonIfUnsupported);
+
+ return result;
+}
+
+template<typename FactoryType, armnn::DataType InputDataType , armnn::DataType OutputDataType>
+bool IsLogicalBinaryLayerBroadcastSupportedTests(std::string& reasonIfUnsupported)
+{
+ armnn::Graph graph;
+ armnn::LogicalBinaryDescriptor desc(armnn::LogicalBinaryOperation::LogicalAnd);
+
+ armnn::Layer* const input0 = graph.AddLayer<armnn::InputLayer>(0, "input0");
+ armnn::Layer* const input1 = graph.AddLayer<armnn::InputLayer>(1, "input1");
+
+ armnn::Layer* const layer = graph.AddLayer<armnn::LogicalBinaryLayer>(desc, "logicalAndLayer");
+
+ armnn::Layer* const output = graph.AddLayer<armnn::OutputLayer>(0, "output2");
+
+ armnn::TensorInfo inputTensorInfo0({1, 1, 1, 4}, InputDataType);
+ armnn::TensorInfo inputTensorInfo1({1, 1, 1, 1}, InputDataType);
+
+ armnn::TensorInfo outputTensorInfo({1, 1, 1, 4}, OutputDataType);
+
+ input0->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
+ input1->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
+
+ input0->GetOutputHandler(0).SetTensorInfo(inputTensorInfo0);
+ input1->GetOutputHandler(0).SetTensorInfo(inputTensorInfo1);
+
+ layer->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+ layer->GetOutputHandler(0).SetTensorInfo(outputTensorInfo);
+
+ bool result = FactoryType::IsLayerSupported(*layer, InputDataType, reasonIfUnsupported);
+
+ return result;
+}
+
+template<typename FactoryType, armnn::DataType InputDataType , armnn::DataType OutputDataType>
bool IsMeanLayerSupportedTests(std::string& reasonIfUnsupported)
{
armnn::Graph graph;
diff --git a/src/backends/neon/NeonLayerSupport.cpp b/src/backends/neon/NeonLayerSupport.cpp
index f55d1c8..2d22576 100644
--- a/src/backends/neon/NeonLayerSupport.cpp
+++ b/src/backends/neon/NeonLayerSupport.cpp
@@ -37,6 +37,9 @@
#include "workloads/NeonInstanceNormalizationWorkload.hpp"
#include "workloads/NeonL2NormalizationFloatWorkload.hpp"
#include "workloads/NeonLogSoftmaxWorkload.hpp"
+#include "workloads/NeonLogicalAndWorkload.hpp"
+#include "workloads/NeonLogicalNotWorkload.hpp"
+#include "workloads/NeonLogicalOrWorkload.hpp"
#include "workloads/NeonLstmFloatWorkload.hpp"
#include "workloads/NeonMaximumWorkload.hpp"
#include "workloads/NeonMeanWorkload.hpp"
@@ -434,6 +437,11 @@
reasonIfUnsupported,
input,
output);
+ case UnaryOperation::LogicalNot:
+ FORWARD_WORKLOAD_VALIDATE_FUNC(NeonLogicalNotWorkloadValidate,
+ reasonIfUnsupported,
+ input,
+ output);
default:
return false;
}
@@ -532,6 +540,31 @@
FORWARD_WORKLOAD_VALIDATE_FUNC(NeonL2NormalizationWorkloadValidate, reasonIfUnsupported, input, output, descriptor);
}
+bool NeonLayerSupport::IsLogicalBinarySupported(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output,
+ const LogicalBinaryDescriptor& descriptor,
+ Optional<std::string&> reasonIfUnsupported) const
+{
+ switch(descriptor.m_Operation)
+ {
+ case LogicalBinaryOperation::LogicalAnd:
+ FORWARD_WORKLOAD_VALIDATE_FUNC(NeonLogicalAndWorkloadValidate,
+ reasonIfUnsupported,
+ input0,
+ input1,
+ output);
+ case LogicalBinaryOperation::LogicalOr:
+ FORWARD_WORKLOAD_VALIDATE_FUNC(NeonLogicalOrWorkloadValidate,
+ reasonIfUnsupported,
+ input0,
+ input1,
+ output);
+ default:
+ return false;
+ }
+}
+
bool NeonLayerSupport::IsLogSoftmaxSupported(const TensorInfo& input,
const TensorInfo& output,
const LogSoftmaxDescriptor& descriptor,
diff --git a/src/backends/neon/NeonLayerSupport.hpp b/src/backends/neon/NeonLayerSupport.hpp
index d477dcd..dc13cc2 100644
--- a/src/backends/neon/NeonLayerSupport.hpp
+++ b/src/backends/neon/NeonLayerSupport.hpp
@@ -160,6 +160,12 @@
const L2NormalizationDescriptor& descriptor,
Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+ bool IsLogicalBinarySupported(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output,
+ const LogicalBinaryDescriptor& descriptor,
+ Optional<std::string&> reasonIfUnsupported) const override;
+
bool IsLogSoftmaxSupported(const TensorInfo& input,
const TensorInfo& output,
const LogSoftmaxDescriptor& descriptor,
diff --git a/src/backends/neon/NeonWorkloadFactory.cpp b/src/backends/neon/NeonWorkloadFactory.cpp
index 709dd93..3077ae0 100644
--- a/src/backends/neon/NeonWorkloadFactory.cpp
+++ b/src/backends/neon/NeonWorkloadFactory.cpp
@@ -260,25 +260,27 @@
switch(descriptor.m_Parameters.m_Operation)
{
case UnaryOperation::Abs:
- {
- AbsQueueDescriptor absQueueDescriptor;
- absQueueDescriptor.m_Inputs = descriptor.m_Inputs;
- absQueueDescriptor.m_Outputs = descriptor.m_Outputs;
+ {
+ AbsQueueDescriptor absQueueDescriptor;
+ absQueueDescriptor.m_Inputs = descriptor.m_Inputs;
+ absQueueDescriptor.m_Outputs = descriptor.m_Outputs;
- return std::make_unique<NeonAbsWorkload>(absQueueDescriptor, info);
- }
+ return std::make_unique<NeonAbsWorkload>(absQueueDescriptor, info);
+ }
case UnaryOperation::Rsqrt:
- {
- RsqrtQueueDescriptor rsqrtQueueDescriptor;
- rsqrtQueueDescriptor.m_Inputs = descriptor.m_Inputs;
- rsqrtQueueDescriptor.m_Outputs = descriptor.m_Outputs;
+ {
+ RsqrtQueueDescriptor rsqrtQueueDescriptor;
+ rsqrtQueueDescriptor.m_Inputs = descriptor.m_Inputs;
+ rsqrtQueueDescriptor.m_Outputs = descriptor.m_Outputs;
- return std::make_unique<NeonRsqrtWorkload>(rsqrtQueueDescriptor, info);
- }
+ return std::make_unique<NeonRsqrtWorkload>(rsqrtQueueDescriptor, info);
+ }
case UnaryOperation::Neg:
return std::make_unique<NeonNegWorkload>(descriptor, info);
case UnaryOperation::Exp:
return std::make_unique<NeonExpWorkload>(descriptor, info);
+ case UnaryOperation::LogicalNot:
+ return std::make_unique<NeonLogicalNotWorkload>(descriptor, info);
default:
return nullptr;
}
@@ -356,6 +358,32 @@
return std::make_unique<NeonLogSoftmaxWorkload>(descriptor, info, m_MemoryManager->GetIntraLayerManager());
}
+std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateLogicalBinary(const LogicalBinaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const
+{
+ switch(descriptor.m_Parameters.m_Operation)
+ {
+ case LogicalBinaryOperation::LogicalAnd:
+ return std::make_unique<NeonLogicalAndWorkload>(descriptor, info);
+ case LogicalBinaryOperation::LogicalOr:
+ return std::make_unique<NeonLogicalOrWorkload>(descriptor, info);
+ default:
+ return nullptr;
+ }
+}
+
+std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateLogicalUnary(const ElementwiseUnaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const
+{
+ switch(descriptor.m_Parameters.m_Operation)
+ {
+ case UnaryOperation::LogicalNot:
+ return std::make_unique<NeonLogicalNotWorkload>(descriptor, info);
+ default:
+ return nullptr;
+ }
+}
+
std::unique_ptr<IWorkload> NeonWorkloadFactory::CreateLstm(const LstmQueueDescriptor& descriptor,
const WorkloadInfo& info) const
{
diff --git a/src/backends/neon/NeonWorkloadFactory.hpp b/src/backends/neon/NeonWorkloadFactory.hpp
index 6a514e2..f98a7f9 100644
--- a/src/backends/neon/NeonWorkloadFactory.hpp
+++ b/src/backends/neon/NeonWorkloadFactory.hpp
@@ -143,6 +143,12 @@
std::unique_ptr<IWorkload> CreateL2Normalization(const L2NormalizationQueueDescriptor& descriptor,
const WorkloadInfo& info) const override;
+ std::unique_ptr<IWorkload> CreateLogicalBinary(const LogicalBinaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const override;
+
+ std::unique_ptr<IWorkload> CreateLogicalUnary(const ElementwiseUnaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info) const override;
+
std::unique_ptr<IWorkload> CreateLogSoftmax(const LogSoftmaxQueueDescriptor& descriptor,
const WorkloadInfo& info) const override;
diff --git a/src/backends/neon/backend.mk b/src/backends/neon/backend.mk
index 9bd08a1..54560cb 100644
--- a/src/backends/neon/backend.mk
+++ b/src/backends/neon/backend.mk
@@ -47,6 +47,9 @@
workloads/NeonGatherWorkload.cpp \
workloads/NeonInstanceNormalizationWorkload.cpp \
workloads/NeonL2NormalizationFloatWorkload.cpp \
+ workloads/NeonLogicalAndWorkload.cpp \
+ workloads/NeonLogicalNotWorkload.cpp \
+ workloads/NeonLogicalOrWorkload.cpp \
workloads/NeonLogSoftmaxWorkload.cpp \
workloads/NeonLstmFloatWorkload.cpp \
workloads/NeonMaximumWorkload.cpp \
diff --git a/src/backends/neon/test/NeonLayerSupportTests.cpp b/src/backends/neon/test/NeonLayerSupportTests.cpp
index 3b086ad..a14122f 100644
--- a/src/backends/neon/test/NeonLayerSupportTests.cpp
+++ b/src/backends/neon/test/NeonLayerSupportTests.cpp
@@ -75,6 +75,26 @@
BOOST_CHECK(result);
}
+BOOST_AUTO_TEST_CASE(IsLogicalBinarySupportedNeon)
+{
+ std::string reasonIfUnsupported;
+
+ bool result = IsLogicalBinaryLayerSupportedTests<armnn::NeonWorkloadFactory,
+ armnn::DataType::Boolean, armnn::DataType::Boolean>(reasonIfUnsupported);
+
+ BOOST_CHECK(result);
+}
+
+BOOST_AUTO_TEST_CASE(IsLogicalBinaryBroadcastSupportedNeon)
+{
+ std::string reasonIfUnsupported;
+
+ bool result = IsLogicalBinaryLayerBroadcastSupportedTests<armnn::NeonWorkloadFactory,
+ armnn::DataType::Boolean, armnn::DataType::Boolean>(reasonIfUnsupported);
+
+ BOOST_CHECK(result);
+}
+
BOOST_AUTO_TEST_CASE(IsMeanSupportedNeon)
{
std::string reasonIfUnsupported;
diff --git a/src/backends/neon/test/NeonLayerTests.cpp b/src/backends/neon/test/NeonLayerTests.cpp
index 59f00fc..8e7742a 100644
--- a/src/backends/neon/test/NeonLayerTests.cpp
+++ b/src/backends/neon/test/NeonLayerTests.cpp
@@ -1323,6 +1323,22 @@
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFillF16, SimpleFillTest<DataType::Float16>)
ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleFillS32, SimpleFillTest<DataType::Signed32>)
+// Logical
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalNot, LogicalNotTest)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalNotInt, LogicalNotIntTest)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAnd, LogicalAndTest)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAndInt, LogicalAndIntTest)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAndBroadcast1, LogicalAndBroadcast1Test)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAndBroadcast2, LogicalAndBroadcast2Test)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAndBroadcast3, LogicalAndBroadcast3Test)
+
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOr, LogicalOrTest)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrInt, LogicalOrIntTest)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrBroadcast1, LogicalOrBroadcast1Test)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrBroadcast2, LogicalOrBroadcast2Test)
+ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrBroadcast3, LogicalOrBroadcast3Test)
+
#if defined(ARMNNREF_ENABLED)
// The ARMNN_COMPARE_REF_AUTO_TEST_CASE and the ARMNN_COMPARE_REF_FIXTURE_TEST_CASE test units are not available
diff --git a/src/backends/neon/workloads/CMakeLists.txt b/src/backends/neon/workloads/CMakeLists.txt
index ca9497e..b03db99 100644
--- a/src/backends/neon/workloads/CMakeLists.txt
+++ b/src/backends/neon/workloads/CMakeLists.txt
@@ -54,10 +54,16 @@
NeonInstanceNormalizationWorkload.hpp
NeonL2NormalizationFloatWorkload.cpp
NeonL2NormalizationFloatWorkload.hpp
- NeonLstmFloatWorkload.cpp
- NeonLstmFloatWorkload.hpp
+ NeonLogicalAndWorkload.cpp
+ NeonLogicalAndWorkload.hpp
+ NeonLogicalNotWorkload.cpp
+ NeonLogicalNotWorkload.hpp
+ NeonLogicalOrWorkload.cpp
+ NeonLogicalOrWorkload.hpp
NeonLogSoftmaxWorkload.cpp
NeonLogSoftmaxWorkload.hpp
+ NeonLstmFloatWorkload.cpp
+ NeonLstmFloatWorkload.hpp
NeonMaximumWorkload.cpp
NeonMaximumWorkload.hpp
NeonMeanWorkload.cpp
diff --git a/src/backends/neon/workloads/NeonLogicalAndWorkload.cpp b/src/backends/neon/workloads/NeonLogicalAndWorkload.cpp
new file mode 100644
index 0000000..d85e05c
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalAndWorkload.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "NeonLogicalAndWorkload.hpp"
+
+#include "NeonWorkloadUtils.hpp"
+
+#include <aclCommon/ArmComputeTensorHandle.hpp>
+#include <aclCommon/ArmComputeTensorUtils.hpp>
+
+#include <armnn/utility/PolymorphicDowncast.hpp>
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalAndWorkloadValidate(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output)
+{
+ const arm_compute::TensorInfo aclInputInfo0 = BuildArmComputeTensorInfo(input0);
+ const arm_compute::TensorInfo aclInputInfo1 = BuildArmComputeTensorInfo(input1);
+ const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output);
+
+ const arm_compute::Status aclStatus = arm_compute::NELogicalAnd::validate(&aclInputInfo0,
+ &aclInputInfo1,
+ &aclOutputInfo);
+ return aclStatus;
+}
+
+NeonLogicalAndWorkload::NeonLogicalAndWorkload(const LogicalBinaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info)
+ : BaseWorkload<LogicalBinaryQueueDescriptor>(descriptor, info)
+{
+ m_Data.ValidateInputsOutputs("NeonLogicalAndWorkload", 2, 1);
+
+ arm_compute::ITensor& input0 = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
+ arm_compute::ITensor& input1 = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[1])->GetTensor();
+ arm_compute::ITensor& output = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
+
+ m_LogicalAndLayer.configure(&input0, &input1, &output);
+}
+
+void NeonLogicalAndWorkload::Execute() const
+{
+ ARMNN_SCOPED_PROFILING_EVENT_NEON("NeonLogicalAndWorkload_Execute");
+ m_LogicalAndLayer.run();
+}
+
+} // namespace armnn
diff --git a/src/backends/neon/workloads/NeonLogicalAndWorkload.hpp b/src/backends/neon/workloads/NeonLogicalAndWorkload.hpp
new file mode 100644
index 0000000..1daadab
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalAndWorkload.hpp
@@ -0,0 +1,30 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <backendsCommon/Workload.hpp>
+
+#include <arm_compute/core/Error.h>
+#include <arm_compute/runtime/NEON/functions/NELogical.h>
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalAndWorkloadValidate(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output);
+
+class NeonLogicalAndWorkload : public BaseWorkload<LogicalBinaryQueueDescriptor>
+{
+public:
+ NeonLogicalAndWorkload(const LogicalBinaryQueueDescriptor& descriptor, const WorkloadInfo& info);
+ virtual void Execute() const override;
+
+private:
+ mutable arm_compute::NELogicalAnd m_LogicalAndLayer;
+};
+
+} //namespace armnn
diff --git a/src/backends/neon/workloads/NeonLogicalNotWorkload.cpp b/src/backends/neon/workloads/NeonLogicalNotWorkload.cpp
new file mode 100644
index 0000000..cff5eaf
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalNotWorkload.cpp
@@ -0,0 +1,48 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "NeonLogicalNotWorkload.hpp"
+
+#include "NeonWorkloadUtils.hpp"
+
+#include <aclCommon/ArmComputeTensorHandle.hpp>
+#include <aclCommon/ArmComputeTensorUtils.hpp>
+
+#include <armnn/utility/PolymorphicDowncast.hpp>
+
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalNotWorkloadValidate(const TensorInfo& input,
+ const TensorInfo& output)
+{
+ const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input);
+ const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output);
+
+ const arm_compute::Status aclStatus = arm_compute::NELogicalNot::validate(&aclInputInfo,
+ &aclOutputInfo);
+ return aclStatus;
+}
+
+NeonLogicalNotWorkload::NeonLogicalNotWorkload(const ElementwiseUnaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info)
+ : BaseWorkload<ElementwiseUnaryQueueDescriptor>(descriptor, info)
+{
+ m_Data.ValidateInputsOutputs("NeonLogicalNotWorkload", 1, 1);
+
+ arm_compute::ITensor& input = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
+ arm_compute::ITensor& output = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
+
+ m_LogicalNotLayer.configure(&input, &output);
+}
+
+void NeonLogicalNotWorkload::Execute() const
+{
+ ARMNN_SCOPED_PROFILING_EVENT_NEON("NeonLogicalNotWorkload_Execute");
+ m_LogicalNotLayer.run();
+}
+
+} // namespace armnn
diff --git a/src/backends/neon/workloads/NeonLogicalNotWorkload.hpp b/src/backends/neon/workloads/NeonLogicalNotWorkload.hpp
new file mode 100644
index 0000000..31420f7
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalNotWorkload.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <backendsCommon/Workload.hpp>
+
+#include <arm_compute/core/Error.h>
+#include <arm_compute/runtime/NEON/functions/NELogical.h>
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalNotWorkloadValidate(const TensorInfo& input, const TensorInfo& output);
+
+class NeonLogicalNotWorkload : public BaseWorkload<ElementwiseUnaryQueueDescriptor>
+{
+public:
+ NeonLogicalNotWorkload(const ElementwiseUnaryQueueDescriptor& descriptor, const WorkloadInfo& info);
+ virtual void Execute() const override;
+
+private:
+ mutable arm_compute::NELogicalNot m_LogicalNotLayer;
+};
+
+} //namespace armnn
diff --git a/src/backends/neon/workloads/NeonLogicalOrWorkload.cpp b/src/backends/neon/workloads/NeonLogicalOrWorkload.cpp
new file mode 100644
index 0000000..c3f21e1
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalOrWorkload.cpp
@@ -0,0 +1,51 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "NeonLogicalOrWorkload.hpp"
+
+#include "NeonWorkloadUtils.hpp"
+
+#include <aclCommon/ArmComputeTensorHandle.hpp>
+#include <aclCommon/ArmComputeTensorUtils.hpp>
+
+#include <armnn/utility/PolymorphicDowncast.hpp>
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalOrWorkloadValidate(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output)
+{
+ const arm_compute::TensorInfo aclInputInfo0 = BuildArmComputeTensorInfo(input0);
+ const arm_compute::TensorInfo aclInputInfo1 = BuildArmComputeTensorInfo(input1);
+ const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output);
+
+ const arm_compute::Status aclStatus = arm_compute::NELogicalOr::validate(&aclInputInfo0,
+ &aclInputInfo1,
+ &aclOutputInfo);
+ return aclStatus;
+}
+
+NeonLogicalOrWorkload::NeonLogicalOrWorkload(const LogicalBinaryQueueDescriptor& descriptor,
+ const WorkloadInfo& info)
+ : BaseWorkload<LogicalBinaryQueueDescriptor>(descriptor, info)
+{
+ m_Data.ValidateInputsOutputs("NeonLogicalOrWorkload", 2, 1);
+
+ arm_compute::ITensor& input0 = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
+ arm_compute::ITensor& input1 = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Inputs[1])->GetTensor();
+ arm_compute::ITensor& output = PolymorphicDowncast<IAclTensorHandle*>(m_Data.m_Outputs[0])->GetTensor();
+
+ m_LogicalOrLayer.configure(&input0, &input1, &output);
+}
+
+void NeonLogicalOrWorkload::Execute() const
+{
+ ARMNN_SCOPED_PROFILING_EVENT_NEON("NeonLogicalOrWorkload_Execute");
+ m_LogicalOrLayer.run();
+}
+
+} // namespace armnn
diff --git a/src/backends/neon/workloads/NeonLogicalOrWorkload.hpp b/src/backends/neon/workloads/NeonLogicalOrWorkload.hpp
new file mode 100644
index 0000000..3b4ddb2
--- /dev/null
+++ b/src/backends/neon/workloads/NeonLogicalOrWorkload.hpp
@@ -0,0 +1,30 @@
+//
+// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <backendsCommon/Workload.hpp>
+
+#include <arm_compute/core/Error.h>
+#include <arm_compute/runtime/NEON/functions/NELogical.h>
+
+namespace armnn
+{
+
+arm_compute::Status NeonLogicalOrWorkloadValidate(const TensorInfo& input0,
+ const TensorInfo& input1,
+ const TensorInfo& output);
+
+class NeonLogicalOrWorkload : public BaseWorkload<LogicalBinaryQueueDescriptor>
+{
+public:
+ NeonLogicalOrWorkload(const LogicalBinaryQueueDescriptor& descriptor, const WorkloadInfo& info);
+ virtual void Execute() const override;
+
+private:
+ mutable arm_compute::NELogicalOr m_LogicalOrLayer;
+};
+
+} //namespace armnn
diff --git a/src/backends/neon/workloads/NeonWorkloads.hpp b/src/backends/neon/workloads/NeonWorkloads.hpp
index 590b6f7..1a17b9a 100644
--- a/src/backends/neon/workloads/NeonWorkloads.hpp
+++ b/src/backends/neon/workloads/NeonWorkloads.hpp
@@ -30,6 +30,9 @@
#include "NeonGatherWorkload.hpp"
#include "NeonInstanceNormalizationWorkload.hpp"
#include "NeonL2NormalizationFloatWorkload.hpp"
+#include "NeonLogicalAndWorkload.hpp"
+#include "NeonLogicalNotWorkload.hpp"
+#include "NeonLogicalOrWorkload.hpp"
#include "NeonLogSoftmaxWorkload.hpp"
#include "NeonLstmFloatWorkload.hpp"
#include "NeonMaximumWorkload.hpp"