IVGCVSW-3546 Create a reference dynamic backend to use for testing and as
an example in the docs

 * Wrapped the reference backend into a dynamic backend
 * Moved the static registration code to a separate file, so that
   it is possible to create the reference dynamic backend that does not
   register statically into armnn
 * Added unit test

Change-Id: I1074d21b020820f9ac8c7178388be773b447555a
Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
diff --git a/src/backends/backends.cmake b/src/backends/backends.cmake
index 438fda3..473de48 100644
--- a/src/backends/backends.cmake
+++ b/src/backends/backends.cmake
@@ -19,3 +19,11 @@
     message("Including backend into the build: ${includeFile}")
     include(${includeFile})
 endforeach()
+
+# parse dynamic backend sub-directories
+file(GLOB dynamicBackendDirs ${PROJECT_SOURCE_DIR}/src/backends/dynamic/*)
+foreach(dynamicBackendDir ${dynamicBackendDirs})
+    if (EXISTS ${dynamicBackendDir} AND IS_DIRECTORY ${dynamicBackendDir})
+        add_subdirectory(${dynamicBackendDir})
+    endif()
+endforeach()
diff --git a/src/backends/backendsCommon/BackendRegistry.cpp b/src/backends/backendsCommon/BackendRegistry.cpp
index 80ab01c..7078304 100644
--- a/src/backends/backendsCommon/BackendRegistry.cpp
+++ b/src/backends/backendsCommon/BackendRegistry.cpp
@@ -17,7 +17,7 @@
 
 void BackendRegistry::Register(const BackendId& id, BackendRegistry::FactoryFunction factory)
 {
-    if (m_Factories.count(id) > 0)
+    if (m_Factories.find(id) != m_Factories.end())
     {
         throw InvalidArgumentException(
             std::string(id) + " already registered as IBackend factory",
diff --git a/src/backends/backendsCommon/test/DynamicBackendTests.cpp b/src/backends/backendsCommon/test/DynamicBackendTests.cpp
index f13c961..e42a08a 100644
--- a/src/backends/backendsCommon/test/DynamicBackendTests.cpp
+++ b/src/backends/backendsCommon/test/DynamicBackendTests.cpp
@@ -61,4 +61,6 @@
 ARMNN_SIMPLE_TEST_CASE(RuntimeInvalidDynamicBackends, RuntimeInvalidDynamicBackendsTestImpl);
 ARMNN_SIMPLE_TEST_CASE(RuntimeInvalidOverridePath, RuntimeInvalidOverridePathTestImpl);
 
+ARMNN_SIMPLE_TEST_CASE(CreateReferenceDynamicBackend, CreateReferenceDynamicBackendTestImpl);
+
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/backends/backendsCommon/test/DynamicBackendTests.hpp b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
index 3a44f50..74ef6f1 100644
--- a/src/backends/backendsCommon/test/DynamicBackendTests.hpp
+++ b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
@@ -7,6 +7,12 @@
 
 #include <backendsCommon/DynamicBackend.hpp>
 #include <backendsCommon/DynamicBackendUtils.hpp>
+#include <backendsCommon/BackendRegistry.hpp>
+#include <backendsCommon/CpuTensorHandle.hpp>
+
+#include <armnn/ILayerSupport.hpp>
+
+#include <reference/workloads/RefConvolution2dWorkload.hpp>
 
 #include <Runtime.hpp>
 
@@ -54,6 +60,10 @@
 static std::string g_TestDynamicBackendsSubDir8  = "backendsTestPath8/";
 static std::string g_TestDynamicBackendsSubDir9  = "backendsTestPath9/";
 
+static std::string g_DynamicBackendsBaseDir                 = "src/backends/dynamic";
+static std::string g_ReferenceDynamicBackendSubDir          = "reference/";
+static std::string g_ReferenceBackendFileName               = "Arm_CpuRef_backend.so";
+
 // DynamicBackendUtils wrapper class used for testing (allows to directly invoke the protected methods)
 class TestDynamicBackendUtils : public armnn::DynamicBackendUtils
 {
@@ -94,17 +104,27 @@
     FactoryStorage m_TempStorage;
 };
 
-std::string GetTestDirectoryBasePath()
+std::string GetBasePath(const std::string& basePath)
 {
     using namespace boost::filesystem;
 
     path programLocation = boost::dll::program_location().parent_path();
-    path sharedObjectPath = programLocation.append(g_TestBaseDir);
+    path sharedObjectPath = programLocation.append(basePath);
     BOOST_CHECK(exists(sharedObjectPath));
 
     return sharedObjectPath.string();
 }
 
+std::string GetTestDirectoryBasePath()
+{
+    return GetBasePath(g_TestBaseDir);
+}
+
+std::string GetDynamicBackendsBasePath()
+{
+    return GetBasePath(g_DynamicBackendsBaseDir);
+}
+
 std::string GetTestSubDirectory(const std::string& subdir)
 {
     using namespace boost::filesystem;
@@ -117,6 +137,17 @@
     return testDynamicBackendsSubDir.string();
 }
 
+std::string GetTestSubDirectory(const std::string& basePath, const std::string& subdir)
+{
+    using namespace boost::filesystem;
+
+    path testDynamicBackendsBasePath(basePath);
+    path testDynamicBackendsSubDir = testDynamicBackendsBasePath.append(subdir);
+    // Do not check that the sub-directory exists because for testing reasons we may use non-existing paths
+
+    return testDynamicBackendsSubDir.string();
+}
+
 std::string GetTestFilePath(const std::string& directory, const std::string& fileName)
 {
     using namespace boost::filesystem;
@@ -1268,3 +1299,82 @@
     const BackendRegistry& backendRegistry = BackendRegistryInstance();
     BOOST_TEST(backendRegistry.Size() == 0);
 }
+
+void CreateReferenceDynamicBackendTestImpl()
+{
+    using namespace armnn;
+    using namespace boost::filesystem;
+
+    // Swapping the backend registry storage for testing
+    TestBackendRegistry testBackendRegistry;
+
+    // This directory contains the reference dynamic backend
+    std::string dynamicBackendsBaseDir = GetDynamicBackendsBasePath();
+    std::string referenceDynamicBackendSubDir = GetTestSubDirectory(dynamicBackendsBaseDir,
+                                                                    g_ReferenceDynamicBackendSubDir);
+    BOOST_CHECK(exists(referenceDynamicBackendSubDir));
+
+    // Check that the reference dynamic backend file exists
+    std::string referenceBackendFilePath = GetTestFilePath(referenceDynamicBackendSubDir,
+                                                           g_ReferenceBackendFileName);
+    BOOST_CHECK(exists(referenceBackendFilePath));
+
+    // Using the path override in CreationOptions to load the reference dynamic backend
+    IRuntime::CreationOptions creationOptions;
+    creationOptions.m_DynamicBackendsPath = referenceDynamicBackendSubDir;
+    IRuntimePtr runtime = IRuntime::Create(creationOptions);
+
+    const BackendRegistry& backendRegistry = BackendRegistryInstance();
+    BOOST_TEST(backendRegistry.Size() == 1);
+
+    BackendIdSet backendIds = backendRegistry.GetBackendIds();
+    BOOST_TEST((backendIds.find("CpuRef") != backendIds.end()));
+
+    // Get the factory function
+    auto referenceDynamicBackendFactoryFunction = backendRegistry.GetFactory("CpuRef");
+    BOOST_TEST((referenceDynamicBackendFactoryFunction != nullptr));
+
+    // Use the factory function to create an instance of the reference backend
+    IBackendInternalUniquePtr referenceDynamicBackend = referenceDynamicBackendFactoryFunction();
+    BOOST_TEST((referenceDynamicBackend != nullptr));
+    BOOST_TEST((referenceDynamicBackend->GetId() == "CpuRef"));
+
+    // Test the backend instance by querying the layer support
+    IBackendInternal::ILayerSupportSharedPtr referenceLayerSupport = referenceDynamicBackend->GetLayerSupport();
+    BOOST_TEST((referenceLayerSupport != nullptr));
+
+    TensorShape inputShape {  1, 16, 16, 16 };
+    TensorShape outputShape{  1, 16, 16, 16 };
+    TensorShape weightShape{ 16,  1,  1, 16 };
+    TensorInfo inputInfo (inputShape,  DataType::Float32);
+    TensorInfo outputInfo(outputShape, DataType::Float32);
+    TensorInfo weightInfo(weightShape, DataType::Float32);
+    Convolution2dDescriptor convolution2dDescriptor;
+    bool referenceConvolution2dSupported =
+            referenceLayerSupport->IsConvolution2dSupported(inputInfo,
+                                                            outputInfo,
+                                                            convolution2dDescriptor,
+                                                            weightInfo,
+                                                            EmptyOptional());
+    BOOST_TEST(referenceConvolution2dSupported);
+
+    // Test the backend instance by creating a workload
+    IBackendInternal::IWorkloadFactoryPtr referenceWorkloadFactory = referenceDynamicBackend->CreateWorkloadFactory();
+    BOOST_TEST((referenceWorkloadFactory != nullptr));
+
+    // Create dummy settings for the workload
+    Convolution2dQueueDescriptor convolution2dQueueDescriptor;
+    WorkloadInfo workloadInfo
+    {
+        { inputInfo },
+        { outputInfo }
+    };
+    convolution2dQueueDescriptor.m_Inputs.push_back(nullptr);
+    auto weights = std::make_unique<ScopedCpuTensorHandle>(weightInfo);
+    convolution2dQueueDescriptor.m_Weight = weights.get();
+
+    // Create a convolution workload with the dummy settings
+    auto workload = referenceWorkloadFactory->CreateConvolution2d(convolution2dQueueDescriptor, workloadInfo);
+    BOOST_TEST((workload != nullptr));
+    BOOST_TEST(workload.get() == boost::polymorphic_downcast<RefConvolution2dWorkload*>(workload.get()));
+}
diff --git a/src/backends/dynamic/reference/CMakeLists.txt b/src/backends/dynamic/reference/CMakeLists.txt
new file mode 100644
index 0000000..e9a94af
--- /dev/null
+++ b/src/backends/dynamic/reference/CMakeLists.txt
@@ -0,0 +1,28 @@
+#
+# Copyright © 2017 Arm Ltd. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+# File needed to wrap the existing backend into a dynamic one
+list(APPEND armnnRefDynamicBackend_sources
+    RefDynamicBackend.cpp
+    RefDynamicBackend.hpp
+)
+
+# Set the backend source path
+set(RefBackendPath ${PROJECT_SOURCE_DIR}/src/backends/reference)
+
+# Source files of the backend, taken directly from the source tree
+file(GLOB RefBackendBaseFiles ${RefBackendPath}/*.cpp)
+set(RefBackendFiles ${RefBackendBaseFiles})
+
+# Remove the file that contains the static backend registration
+list(REMOVE_ITEM RefBackendFiles ${RefBackendPath}/RefRegistryInitializer.cpp)
+
+# Create the shared object
+add_library(Arm_CpuRef_backend MODULE ${armnnRefDynamicBackend_sources} ${RefBackendFiles})
+target_include_directories(Arm_CpuRef_backend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn)
+target_include_directories(Arm_CpuRef_backend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils)
+target_include_directories(Arm_CpuRef_backend PRIVATE ${PROJECT_SOURCE_DIR}/src/backends)
+set_target_properties(Arm_CpuRef_backend PROPERTIES PREFIX "")
+target_link_libraries(Arm_CpuRef_backend armnn)
diff --git a/src/backends/dynamic/reference/RefDynamicBackend.cpp b/src/backends/dynamic/reference/RefDynamicBackend.cpp
new file mode 100644
index 0000000..5920432
--- /dev/null
+++ b/src/backends/dynamic/reference/RefDynamicBackend.cpp
@@ -0,0 +1,33 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RefDynamicBackend.hpp"
+
+#include <reference/RefBackend.hpp>
+
+using namespace armnn;
+
+const char* GetBackendId()
+{
+    return RefBackend::GetIdStatic().Get().c_str();
+}
+
+void GetVersion(uint32_t* outMajor, uint32_t* outMinor)
+{
+    if (!outMajor || !outMinor)
+    {
+        return;
+    }
+
+    BackendVersion apiVersion = IBackendInternal::GetApiVersion();
+
+    *outMajor = apiVersion.m_Major;
+    *outMinor = apiVersion.m_Minor;
+}
+
+void* BackendFactory()
+{
+    return new RefBackend();
+}
diff --git a/src/backends/dynamic/reference/RefDynamicBackend.hpp b/src/backends/dynamic/reference/RefDynamicBackend.hpp
new file mode 100644
index 0000000..b058cd2
--- /dev/null
+++ b/src/backends/dynamic/reference/RefDynamicBackend.hpp
@@ -0,0 +1,15 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <cstdint>
+
+extern "C"
+{
+const char* GetBackendId();
+void GetVersion(uint32_t* outMajor, uint32_t* outMinor);
+void* BackendFactory();
+}
diff --git a/src/backends/reference/CMakeLists.txt b/src/backends/reference/CMakeLists.txt
index 281e916..963e643 100644
--- a/src/backends/reference/CMakeLists.txt
+++ b/src/backends/reference/CMakeLists.txt
@@ -13,6 +13,7 @@
     RefLayerSupport.hpp
     RefMemoryManager.hpp
     RefMemoryManager.cpp
+    RefRegistryInitializer.cpp
     RefWorkloadFactory.cpp
     RefWorkloadFactory.hpp
 )
diff --git a/src/backends/reference/RefBackend.cpp b/src/backends/reference/RefBackend.cpp
index 3680831..41438b0 100644
--- a/src/backends/reference/RefBackend.cpp
+++ b/src/backends/reference/RefBackend.cpp
@@ -20,21 +20,6 @@
 namespace armnn
 {
 
-namespace
-{
-
-static BackendRegistry::StaticRegistryInitializer g_RegisterHelper
-{
-    BackendRegistryInstance(),
-    RefBackend::GetIdStatic(),
-    []()
-    {
-        return IBackendInternalUniquePtr(new RefBackend);
-    }
-};
-
-}
-
 const BackendId& RefBackend::GetIdStatic()
 {
     static const BackendId s_Id{RefBackendId()};
diff --git a/src/backends/reference/RefRegistryInitializer.cpp b/src/backends/reference/RefRegistryInitializer.cpp
new file mode 100644
index 0000000..427c7f0
--- /dev/null
+++ b/src/backends/reference/RefRegistryInitializer.cpp
@@ -0,0 +1,25 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RefBackend.hpp"
+
+#include <backendsCommon/BackendRegistry.hpp>
+
+namespace
+{
+
+using namespace armnn;
+
+static BackendRegistry::StaticRegistryInitializer g_RegisterHelper
+{
+    BackendRegistryInstance(),
+    RefBackend::GetIdStatic(),
+    []()
+    {
+        return IBackendInternalUniquePtr(new RefBackend);
+    }
+};
+
+} // Anonymous namespace
diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk
index 6e1360a..b212995 100644
--- a/src/backends/reference/backend.mk
+++ b/src/backends/reference/backend.mk
@@ -13,6 +13,7 @@
         RefMemoryManager.cpp \
         RefTensorHandle.cpp \
         RefWorkloadFactory.cpp \
+        RefRegistryInitializer.cpp \
         workloads/Activation.cpp \
         workloads/BatchNormImpl.cpp \
         workloads/BatchToSpaceNd.cpp \