IVGCVSW-5525 Handle Neon optionality on 32 bit linux platforms

 * Add neon detection for linux using HWCAPs
 * Add test to check for backend throwing BackendUnavailable exception

Signed-off-by: Francis Murtagh <francis.murtagh@arm.com>
Change-Id: Ib74aeb06abe5f88f21ecdd1edb2a1cd20ee2019d
diff --git a/include/armnn/Utils.hpp b/include/armnn/Utils.hpp
index 773a403..efd3d21 100644
--- a/include/armnn/Utils.hpp
+++ b/include/armnn/Utils.hpp
@@ -35,4 +35,6 @@
 #   define ARMNN_FALLTHROUGH ((void)0)
 #endif
 
+bool NeonDetected();
+
 } // namespace armnn
diff --git a/src/armnn/Utils.cpp b/src/armnn/Utils.cpp
index 13bf302..62c8ae6 100644
--- a/src/armnn/Utils.cpp
+++ b/src/armnn/Utils.cpp
@@ -5,6 +5,13 @@
 #include "armnn/Logging.hpp"
 #include "armnn/Utils.hpp"
 
+#if !defined(BARE_METAL) && (defined(__arm__) || defined(__aarch64__))
+
+#include <sys/auxv.h>
+#include <asm/hwcap.h>
+
+#endif
+
 namespace armnn
 {
 void ConfigureLogging(bool printToStandardOutput, bool printToDebugOutput, LogSeverity severity)
@@ -25,4 +32,45 @@
 
 static DefaultLoggingConfiguration g_DefaultLoggingConfiguration;
 
+// Detect the presence of Neon on Linux
+bool NeonDetected()
+{
+#if !defined(BARE_METAL) && (defined(__arm__) || defined(__aarch64__))
+    auto hwcaps= getauxval(AT_HWCAP);
+#endif
+
+#if !defined(BARE_METAL) && defined(__aarch64__)
+
+    if (hwcaps & HWCAP_ASIMD)
+    {
+        // On an arm64 device with Neon.
+        return true;
+    }
+    else
+    {
+        // On an arm64 device without Neon.
+        return false;
+    }
+
+#endif
+#if !defined(BARE_METAL) && defined(__arm__)
+
+    if (hwcaps & HWCAP_NEON)
+    {
+        // On an armhf device with Neon.
+        return true;
+    }
+    else
+    {
+        // On an armhf device without Neon.
+        return false;
+    }
+
+#endif
+
+    // This method of Neon detection is only supported on Linux so in order to prevent a false negative
+    // we will return true in cases where detection did not run.
+    return true;
+}
+
 } // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/test/BackendRegistryTests.cpp b/src/backends/backendsCommon/test/BackendRegistryTests.cpp
index 213d114..ce8acbb 100644
--- a/src/backends/backendsCommon/test/BackendRegistryTests.cpp
+++ b/src/backends/backendsCommon/test/BackendRegistryTests.cpp
@@ -7,6 +7,7 @@
 #include <armnn/BackendRegistry.hpp>
 
 #include <armnn/backends/IBackendInternal.hpp>
+#include <reference/RefBackend.hpp>
 
 #include <boost/test/unit_test.hpp>
 
@@ -103,4 +104,47 @@
     BackendRegistryInstance().Deregister("HelloWorld");
 }
 
+// Test that backends can throw exceptions during their factory function to prevent loading in an unsuitable
+// environment. For example Neon Backend loading on armhf device without neon support.
+// In reality the dynamic backend is loaded in during the LoadDynamicBackends(options.m_DynamicBackendsPath)
+// step of runtime constructor, then the factory function is called to check if supported, in case
+// of Neon not being detected the exception is raised and so the backend is not added to the supportedBackends
+// list
+
+BOOST_AUTO_TEST_CASE(ThrowBackendUnavailableException)
+{
+    using namespace armnn;
+
+    const BackendId mockBackendId("MockDynamicBackend");
+
+    const std::string exceptionMessage("Neon support not found on device, could not register CpuAcc Backend.\n");
+
+    // Register the mock backend with a factory function lambda equivalent to NeonRegisterInitializer
+    BackendRegistryInstance().Register(mockBackendId,
+            [exceptionMessage]()
+            {
+                if (false)
+                {
+                    return IBackendInternalUniquePtr(new RefBackend);
+                }
+                ARMNN_LOG(info) << "Neon support not found on device, could not register CpuAcc Backend.";
+                throw armnn::BackendUnavailableException(exceptionMessage);
+            });
+
+    // Get the factory function of the mock backend
+    auto factoryFunc = BackendRegistryInstance().GetFactory(mockBackendId);
+
+    try
+    {
+        // Call the factory function as done during runtime backend registering
+        auto backend = factoryFunc();
+    }
+    catch (const BackendUnavailableException& e)
+    {
+        // Caught
+        BOOST_CHECK_EQUAL(e.what(), exceptionMessage);
+        BOOST_TEST_MESSAGE("ThrowBackendUnavailableExceptionImpl: BackendUnavailableException caught.");
+    }
+}
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/backends/neon/NeonRegistryInitializer.cpp b/src/backends/neon/NeonRegistryInitializer.cpp
index fd2e84d..fc981ab 100644
--- a/src/backends/neon/NeonRegistryInitializer.cpp
+++ b/src/backends/neon/NeonRegistryInitializer.cpp
@@ -6,6 +6,7 @@
 #include "NeonBackend.hpp"
 
 #include <armnn/BackendRegistry.hpp>
+#include <armnn/Utils.hpp>
 
 namespace
 {
@@ -18,7 +19,16 @@
     NeonBackend::GetIdStatic(),
     []()
     {
-        return IBackendInternalUniquePtr(new NeonBackend);
+        // Check if device supports Neon.
+        if (NeonDetected())
+        {
+            return IBackendInternalUniquePtr(new NeonBackend);
+        }
+
+        // If device does not support Neon throw exception so the Backend is not added to supportedBackends
+        ARMNN_LOG(info) << "Neon support not found on device, could not register CpuAcc Backend.";
+        throw armnn::BackendUnavailableException(
+                "Neon support not found on device, could not register CpuAcc Backend.\n");
     }
 };