summaryrefslogtreecommitdiff
path: root/runtimes/neurun/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'runtimes/neurun/src/backend')
-rw-r--r--runtimes/neurun/src/backend/BackendManager.cc88
-rw-r--r--runtimes/neurun/src/backend/BackendManager.h73
-rw-r--r--runtimes/neurun/src/backend/CMakeLists.txt2
-rw-r--r--runtimes/neurun/src/backend/IBackendConfig.h39
-rw-r--r--runtimes/neurun/src/backend/IInitializerGenerator.h46
-rw-r--r--runtimes/neurun/src/backend/IObject.h42
-rw-r--r--runtimes/neurun/src/backend/IStageGenerator.h68
-rw-r--r--runtimes/neurun/src/backend/ITensorBuilder.h57
-rw-r--r--runtimes/neurun/src/backend/acl_cl/BackendConfig.cc32
-rw-r--r--runtimes/neurun/src/backend/acl_cl/BackendConfig.h45
-rw-r--r--runtimes/neurun/src/backend/acl_cl/CMakeLists.txt17
-rw-r--r--runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc144
-rw-r--r--runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h50
-rw-r--r--runtimes/neurun/src/backend/acl_cl/StageGenerator.cc538
-rw-r--r--runtimes/neurun/src/backend/acl_cl/StageGenerator.h58
-rw-r--r--runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc79
-rw-r--r--runtimes/neurun/src/backend/acl_cl/TensorBuilder.h57
-rw-r--r--runtimes/neurun/src/backend/acl_cl/feature/View.h110
-rw-r--r--runtimes/neurun/src/backend/acl_cl/kernel/View.h87
-rw-r--r--runtimes/neurun/src/backend/acl_cl/operand/Object.cc42
-rw-r--r--runtimes/neurun/src/backend/acl_cl/operand/Object.h60
-rw-r--r--runtimes/neurun/src/backend/cpu/BackendConfig.cc33
-rw-r--r--runtimes/neurun/src/backend/cpu/BackendConfig.h45
-rw-r--r--runtimes/neurun/src/backend/cpu/CMakeLists.txt19
-rw-r--r--runtimes/neurun/src/backend/cpu/InitializerGenerator.cc208
-rw-r--r--runtimes/neurun/src/backend/cpu/InitializerGenerator.h50
-rw-r--r--runtimes/neurun/src/backend/cpu/MemoryAllocator.cc17
-rw-r--r--runtimes/neurun/src/backend/cpu/MemoryAllocator.h123
-rw-r--r--runtimes/neurun/src/backend/cpu/StageGenerator.cc536
-rw-r--r--runtimes/neurun/src/backend/cpu/StageGenerator.h59
-rw-r--r--runtimes/neurun/src/backend/cpu/TensorBuilder.cc73
-rw-r--r--runtimes/neurun/src/backend/cpu/TensorBuilder.h57
-rw-r--r--runtimes/neurun/src/backend/cpu/operand/Object.cc36
-rw-r--r--runtimes/neurun/src/backend/cpu/operand/Object.h60
-rw-r--r--runtimes/neurun/src/backend/cpu/operand/Tensor.cc33
-rw-r--r--runtimes/neurun/src/backend/cpu/operand/Tensor.h72
36 files changed, 3155 insertions, 0 deletions
diff --git a/runtimes/neurun/src/backend/BackendManager.cc b/runtimes/neurun/src/backend/BackendManager.cc
new file mode 100644
index 000000000..fb7d69108
--- /dev/null
+++ b/runtimes/neurun/src/backend/BackendManager.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BackendManager.h"
+
+#include "backend/acl_cl/BackendConfig.h"
+#include "backend/acl_cl/TensorBuilder.h"
+#include "backend/acl_cl/InitializerGenerator.h"
+#include "backend/acl_cl/StageGenerator.h"
+#include "backend/cpu/BackendConfig.h"
+#include "backend/cpu/TensorBuilder.h"
+#include "backend/cpu/InitializerGenerator.h"
+#include "backend/cpu/StageGenerator.h"
+
+namespace neurun
+{
+namespace backend
+{
+
+Backend::Backend(const std::shared_ptr<neurun::backend::IBackendConfig> &backend_config,
+ const std::shared_ptr<neurun::backend::IInitializerGenerator> &initializer_gen,
+ const std::shared_ptr<neurun::backend::IStageGenerator> &stage_gen)
+ : _config(backend_config), _initializer_gen(initializer_gen), _stage_gen(stage_gen)
+{
+ backend_config->initialize();
+}
+
+const std::shared_ptr<neurun::backend::IBackendConfig> Backend::config() const { return _config; }
+
+const std::shared_ptr<neurun::backend::IInitializerGenerator> Backend::initializer_gen() const
+{
+ return _initializer_gen;
+}
+
+const std::shared_ptr<neurun::backend::IStageGenerator> Backend::stage_gen() const
+{
+ return _stage_gen;
+}
+
+const std::shared_ptr<neurun::backend::ITensorBuilder> Backend::tensor_builder() const
+{
+ return _stage_gen->tensor_builder();
+}
+
+BackendManager::BackendManager(const neurun::graph::operand::Set &operands)
+{
+ // Add arm_compute backend
+ {
+ using namespace ::neurun::backend::acl_cl;
+ auto acl_backend_initializer = std::make_shared<BackendConfig>();
+ auto acl_tensor_builder = std::make_shared<TensorBuilder>();
+ auto acl_initializer_gen = std::make_shared<InitializerGenerator>(operands);
+ auto acl_stage_gen = std::make_shared<StageGenerator>(operands, acl_tensor_builder);
+
+ // TODO Do not use magic string for backend id
+ _gen_map["acl_cl"] = {acl_backend_initializer, acl_initializer_gen, acl_stage_gen};
+ }
+
+ // Add CPU backend
+ {
+ using namespace ::neurun::backend::cpu;
+ auto cpu_backend_initializer = std::make_shared<BackendConfig>();
+ auto cpu_tensor_builder = std::make_shared<TensorBuilder>();
+ auto cpu_initializer_gen = std::make_shared<InitializerGenerator>(operands);
+ auto cpu_stage_gen = std::make_shared<StageGenerator>(operands, cpu_tensor_builder);
+
+ // TODO Do not use magic string for backend id
+ _gen_map["cpu"] = {cpu_backend_initializer, cpu_initializer_gen, cpu_stage_gen};
+ }
+}
+
+Backend BackendManager::get(const std::string &key) { return _gen_map.at(key); }
+
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/BackendManager.h b/runtimes/neurun/src/backend/BackendManager.h
new file mode 100644
index 000000000..6f862ffe6
--- /dev/null
+++ b/runtimes/neurun/src/backend/BackendManager.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_BACKEND_MANAGER_H__
+#define __NEURUN_BACKEND_BACKEND_MANAGER_H__
+
+#include <memory>
+#include <map>
+
+#include "graph/operand/Set.h"
+
+namespace neurun
+{
+namespace backend
+{
+
+struct IBackendConfig;
+struct IInitializerGenerator;
+struct IStageGenerator;
+struct ITensorBuilder;
+
+class Backend
+{
+public:
+ Backend(const std::shared_ptr<neurun::backend::IBackendConfig> &backend_config,
+ const std::shared_ptr<neurun::backend::IInitializerGenerator> &initializer_gen,
+ const std::shared_ptr<neurun::backend::IStageGenerator> &stage_gen);
+
+ Backend(void) : _config(nullptr), _initializer_gen(nullptr), _stage_gen(nullptr)
+ {
+ // DO NOTHING
+ }
+
+public:
+ const std::shared_ptr<neurun::backend::IBackendConfig> config() const;
+ const std::shared_ptr<neurun::backend::IInitializerGenerator> initializer_gen() const;
+ const std::shared_ptr<neurun::backend::IStageGenerator> stage_gen() const;
+ const std::shared_ptr<neurun::backend::ITensorBuilder> tensor_builder() const;
+
+private:
+ std::shared_ptr<neurun::backend::IBackendConfig> _config;
+ std::shared_ptr<neurun::backend::IInitializerGenerator> _initializer_gen;
+ std::shared_ptr<neurun::backend::IStageGenerator> _stage_gen;
+};
+
+class BackendManager
+{
+public:
+ BackendManager(const neurun::graph::operand::Set &operands);
+
+ Backend get(const std::string &key);
+
+private:
+ std::map<std::string, Backend> _gen_map;
+};
+
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_BACKEND_MANAGER_H__
diff --git a/runtimes/neurun/src/backend/CMakeLists.txt b/runtimes/neurun/src/backend/CMakeLists.txt
new file mode 100644
index 000000000..a39823102
--- /dev/null
+++ b/runtimes/neurun/src/backend/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(cpu)
+add_subdirectory(acl_cl)
diff --git a/runtimes/neurun/src/backend/IBackendConfig.h b/runtimes/neurun/src/backend/IBackendConfig.h
new file mode 100644
index 000000000..a6c7ce517
--- /dev/null
+++ b/runtimes/neurun/src/backend/IBackendConfig.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_IBACKEND_CONFIG_H__
+#define __INTERNAL_IBACKEND_CONFIG_H__
+
+#include "graph/operand/Layout.h"
+
+namespace neurun
+{
+namespace backend
+{
+
+struct IBackendConfig
+{
+ virtual ~IBackendConfig() = default;
+
+ virtual void initialize() = 0;
+ // NOTE Assume backend has only one type of operand layout
+ virtual graph::operand::Layout getOperandLayout() = 0;
+};
+
+} // namespace backend
+} // namespace neurun
+
+#endif // __INTERNAL_IBACKEND_CONFIG_H__
diff --git a/runtimes/neurun/src/backend/IInitializerGenerator.h b/runtimes/neurun/src/backend/IInitializerGenerator.h
new file mode 100644
index 000000000..83cf87a52
--- /dev/null
+++ b/runtimes/neurun/src/backend/IInitializerGenerator.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_IINITIALIZER_GENERATOR_H__
+#define __INTERNAL_IINITIALIZER_GENERATOR_H__
+
+#include "arm_compute/core/ITensor.h"
+
+#include "graph/operation/Conv2D.h"
+#include "graph/operation/FullyConnected.h"
+
+using Initializer = std::function<void(::arm_compute::ITensor &)>;
+
+namespace neurun
+{
+namespace backend
+{
+
+struct IInitializerGenerator
+{
+ virtual ~IInitializerGenerator() = default;
+
+ virtual Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) = 0;
+ virtual Initializer generateWeight(const graph::operation::FullyConnected::Node &node) = 0;
+
+ virtual Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) = 0;
+ virtual Initializer generateBias(const graph::operation::FullyConnected::Node &node) = 0;
+};
+
+} // namespace backend
+} // namespace neurun
+
+#endif // __INTERNAL_IINITIALIZER_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/IObject.h b/runtimes/neurun/src/backend/IObject.h
new file mode 100644
index 000000000..f7d511095
--- /dev/null
+++ b/runtimes/neurun/src/backend/IObject.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_OPERAND_I_OBJECT_H__
+#define __NEURUN_BACKEND_OPERAND_I_OBJECT_H__
+
+#include <functional>
+
+#include <arm_compute/core/ITensor.h>
+
+namespace neurun
+{
+namespace backend
+{
+namespace operand
+{
+
+struct IObject
+{
+ virtual ~IObject() = default;
+ virtual ::arm_compute::ITensor *ptr(void) const = 0;
+ virtual void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const = 0;
+};
+
+} // namespace operand
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_OPERAND_I_OBJECT_H__
diff --git a/runtimes/neurun/src/backend/IStageGenerator.h b/runtimes/neurun/src/backend/IStageGenerator.h
new file mode 100644
index 000000000..05959e2b1
--- /dev/null
+++ b/runtimes/neurun/src/backend/IStageGenerator.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_ISTAGE_GENERATOR_H__
+#define __INTERNAL_ISTAGE_GENERATOR_H__
+
+#include <memory>
+#include <functional>
+
+#include <arm_compute/runtime/IFunction.h>
+
+#include "backend/ITensorBuilder.h"
+#include "graph/operation/Conv2D.h"
+#include "graph/operation/MaxPool2D.h"
+#include "graph/operation/AvgPool2D.h"
+#include "graph/operation/Concat.h"
+#include "graph/operation/FullyConnected.h"
+#include "graph/operation/Reshape.h"
+#include "graph/operation/Softmax.h"
+#include "graph/operation/NOP.h"
+
+struct IExecutionBuilder
+{
+ virtual ~IExecutionBuilder() = default;
+
+ virtual void append(std::unique_ptr<::arm_compute::IFunction> &&f) = 0;
+};
+
+using Stage = std::function<void(IExecutionBuilder &)>;
+
+namespace neurun
+{
+namespace backend
+{
+
+struct IStageGenerator
+{
+ virtual ~IStageGenerator() = default;
+
+ virtual std::shared_ptr<ITensorBuilder> tensor_builder() = 0;
+
+ virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) = 0;
+ virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) = 0;
+ virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) = 0;
+ virtual Stage generate(const graph::operation::Concat::Node &node) = 0;
+ virtual Stage generate(const graph::operation::FullyConnected::Node &node) = 0;
+ virtual Stage generate(const graph::operation::Reshape::Node &node) = 0;
+ virtual Stage generate(const graph::operation::Softmax::Node &node) = 0;
+ virtual Stage generate(const graph::operation::NOP::Node &node) = 0;
+};
+
+} // namespace backend
+} // namespace neurun
+
+#endif // __INTERNAL_ISTAGE_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/ITensorBuilder.h b/runtimes/neurun/src/backend/ITensorBuilder.h
new file mode 100644
index 000000000..c3a07ffeb
--- /dev/null
+++ b/runtimes/neurun/src/backend/ITensorBuilder.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_ITENSOR_BUILDER_H__
+#define __INTERNAL_ITENSOR_BUILDER_H__
+
+#include <map>
+#include <arm_compute/core/TensorInfo.h>
+
+#include "graph/operand/Index.h"
+#include "codegen/Plan.h"
+
+namespace neurun
+{
+namespace backend
+{
+
+struct ITensorBuilder
+{
+ virtual ~ITensorBuilder(void) = default;
+ virtual void mark(const ::neurun::graph::operand::Index &ind) = 0;
+ // TODO Add an interface for adding subsumption info
+ virtual void prepare(codegen::Plan &plan,
+ const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) = 0;
+ virtual void allocate(void) = 0;
+};
+
+} // namespace backend
+} // namespace neurun
+
+#include <set>
+#include <memory>
+
+namespace neurun
+{
+namespace backend
+{
+
+using TensorBuilderSet = std::set<std::shared_ptr<backend::ITensorBuilder>>;
+
+} // namespace backend
+} // namespace neurun
+
+#endif // __INTERNAL_ITENSOR_BUILDER_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc b/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc
new file mode 100644
index 000000000..6b3e6b3a3
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/BackendConfig.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arm_compute/runtime/CL/CLScheduler.h>
+
+#include "backend/acl_cl/BackendConfig.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+void BackendConfig::initialize() { arm_compute::CLScheduler::get().default_init(); }
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/acl_cl/BackendConfig.h b/runtimes/neurun/src/backend/acl_cl/BackendConfig.h
new file mode 100644
index 000000000..8eec9e795
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/BackendConfig.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__
+#define __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__
+
+#include "backend/IBackendConfig.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+class BackendConfig : public IBackendConfig
+{
+public:
+ BackendConfig()
+ {
+ // DO NOTHING
+ }
+
+ virtual void initialize() override;
+ virtual graph::operand::Layout getOperandLayout() { return graph::operand::Layout::NCHW; }
+};
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_ACL_CL_BACKEND_CONFIG_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt b/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt
new file mode 100644
index 000000000..d64c23a80
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/CMakeLists.txt
@@ -0,0 +1,17 @@
+file(GLOB_RECURSE SOURCES "*.cc")
+
+add_library(${LIB_NEURUN_BACKEND_ACL_CL} STATIC ${SOURCES})
+
+target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${NNFW_INCLUDE_DIR})
+target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${NEURUN_INCLUDE_DIR})
+target_include_directories(${LIB_NEURUN_BACKEND_ACL_CL} PUBLIC ${CMAKE_SOURCE_DIR}/externals/tensorflow) # TODO Remove this file. We should not need this.
+
+target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} arm_compute)
+target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} nnfw_support_nnapi)
+target_link_libraries(${LIB_NEURUN_BACKEND_ACL_CL} ${LIB_NEURUN_KERNEL_ACL_CL})
+
+target_compile_options(${LIB_NEURUN_BACKEND_ACL_CL} PRIVATE -Wall -Wextra -Werror)
+
+set_target_properties(${LIB_NEURUN_BACKEND_ACL_CL} PROPERTIES POSITION_INDEPENDENT_CODE ON)
+set_target_properties(${LIB_NEURUN_BACKEND_ACL_CL} PROPERTIES OUTPUT_NAME backend_acl_cl)
+install(TARGETS ${LIB_NEURUN_BACKEND_ACL_CL} DESTINATION lib/neurun)
diff --git a/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc
new file mode 100644
index 000000000..9a681b3de
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend/acl_cl/InitializerGenerator.h"
+
+#include <arm_compute/core/Coordinates.h>
+
+#include "backend/acl_cl/kernel/View.h"
+#include "internal/nnapi/kernel/Reader.h"
+#include "util/kernel/IndexIterator.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+InitializerGenerator::InitializerGenerator(const neurun::graph::operand::Set &ctx) : _ctx(ctx)
+{
+ // DO NOTHING
+}
+
+Initializer
+InitializerGenerator::generateWeight(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)};
+
+ const auto ker_shape = _ctx.at(ker_index).shape().asKernel();
+ auto ker_base = _ctx.at(ker_index).data().base();
+ auto ker_size = _ctx.at(ker_index).data().size();
+
+ return [ker_shape, ker_base, ker_size](::arm_compute::ITensor &tensor) {
+ const ::internal::nnapi::kernel::Reader<float> from{ker_shape, ker_base, ker_size};
+ ::internal::arm_compute::kernel::View<float> into{&tensor};
+
+ ::nnfw::util::kernel::iterate(ker_shape)
+ << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+ const auto value = from.at(nth, ch, row, col);
+ into.at(nth, ch, row, col) = value;
+ };
+ };
+}
+
+Initializer InitializerGenerator::generateWeight(const graph::operation::FullyConnected::Node &node)
+{
+ const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+
+ const auto num_output = _ctx.at(weight_index).shape().dim(0);
+ auto weight_base = _ctx.at(weight_index).data().base();
+ auto weight_size = _ctx.at(weight_index).data().size();
+
+ // NOTE We assume that input is a feature map
+ // TODO Remove this restriction!
+ const auto ifm_shape = _ctx.at(input_index).shape().asFeature();
+
+ return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) {
+ const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H, ifm_shape.W};
+ const ::internal::nnapi::kernel::Reader<float> from{ker_shape, weight_base, weight_size};
+
+ ::nnfw::util::kernel::iterate(ker_shape)
+ << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+ const auto value = from.at(nth, ch, row, col);
+
+ uint32_t offset = 0;
+
+ // ARM Compute Library uses 'NCHW' ordering
+ offset += nth * ifm_shape.C * ifm_shape.H * ifm_shape.W;
+ offset += ch * ifm_shape.H * ifm_shape.W;
+ offset += row * ifm_shape.W;
+ offset += col;
+
+ const ::arm_compute::Coordinates coordinate{offset};
+
+ auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ *into = value;
+ };
+ };
+}
+
+Initializer InitializerGenerator::generateBias(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ // TODO Refactor so we can reuse the common code
+
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ auto bias_base = _ctx.at(bias_index).data().base();
+ const auto bias_size = _ctx.at(bias_index).shape().asVector();
+
+ return [bias_base, bias_size](::arm_compute::ITensor &tensor) {
+ for (int32_t n = 0; n < bias_size; ++n)
+ {
+ const ::arm_compute::Coordinates coordinate{n};
+
+ float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ const float *from = reinterpret_cast<const float *>(bias_base) + n;
+ const auto value = *from;
+
+ *into = value;
+ }
+ };
+}
+
+Initializer InitializerGenerator::generateBias(const graph::operation::FullyConnected::Node &node)
+{
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ auto bias_base = _ctx.at(bias_index).data().base();
+ const auto bias_size = _ctx.at(bias_index).shape().asVector();
+
+ return [bias_base, bias_size](::arm_compute::ITensor &tensor) {
+ for (int32_t n = 0; n < bias_size; ++n)
+ {
+ const ::arm_compute::Coordinates coordinate{n};
+
+ float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ const float *from = reinterpret_cast<const float *>(bias_base) + n;
+ const auto value = *from;
+
+ *into = value;
+ }
+ };
+}
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h
new file mode 100644
index 000000000..78b7efb5e
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/InitializerGenerator.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__
+#define __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__
+
+#include "backend/IInitializerGenerator.h"
+
+#include "graph/operand/Set.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+class InitializerGenerator : public IInitializerGenerator
+{
+public:
+ InitializerGenerator(const neurun::graph::operand::Set &ctx);
+
+ Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) override;
+ Initializer generateWeight(const graph::operation::FullyConnected::Node &node) override;
+
+ Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) override;
+ Initializer generateBias(const graph::operation::FullyConnected::Node &node) override;
+
+private:
+ const neurun::graph::operand::Set &_ctx;
+};
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_ACL_CL_INITIALIZER_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc b/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc
new file mode 100644
index 000000000..c63698fd8
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/StageGenerator.cc
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend/acl_cl/StageGenerator.h"
+
+#include <arm_compute/runtime/CL/functions/CLConvolutionLayer.h>
+#include <arm_compute/runtime/CL/functions/CLPoolingLayer.h>
+#include <arm_compute/runtime/CL/functions/CLActivationLayer.h>
+#include <arm_compute/runtime/CL/functions/CLReshapeLayer.h>
+#include <arm_compute/runtime/CL/functions/CLFullyConnectedLayer.h>
+#include <arm_compute/runtime/CL/functions/CLSoftmaxLayer.h>
+
+#include "kernel/acl_cl/ConcatLayer.h"
+
+#include "internal/Padding.h"
+
+#include "graph/operand/Index.h"
+
+#include "logging.h"
+
+#include "NeuralNetworks.h"
+
+#include "support/nnapi/Utils.h"
+
+template <typename T> std::unique_ptr<T> make_layer(void) { return std::unique_ptr<T>{new T}; }
+
+::arm_compute::PadStrideInfo asPadStringInfo(const ::internal::Padding &padding,
+ const ::internal::Stride &stride)
+{
+ return ::arm_compute::PadStrideInfo{stride.horizontal,
+ stride.vertical,
+ padding.left,
+ padding.right,
+ padding.top,
+ padding.bottom,
+ ::arm_compute::DimensionRoundingType::FLOOR};
+}
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+//
+// ActivationBuilder
+//
+class ActivationBuilder
+{
+public:
+ ActivationBuilder(IExecutionBuilder &builder) : _builder(builder)
+ {
+ // DO NOTHING
+ }
+
+private:
+ void appendReLU(::arm_compute::ICLTensor *tensor);
+
+public:
+ void append(FuseCode code, ::arm_compute::ICLTensor *tensor);
+
+private:
+ IExecutionBuilder &_builder;
+};
+
+void ActivationBuilder::appendReLU(::arm_compute::ICLTensor *ifm_alloc)
+{
+ const ::arm_compute::ActivationLayerInfo act_info{
+ ::arm_compute::ActivationLayerInfo::ActivationFunction::RELU};
+
+ auto fn = make_layer<::arm_compute::CLActivationLayer>();
+
+ fn->configure(ifm_alloc, nullptr, act_info);
+
+ _builder.append(std::move(fn));
+}
+
+void ActivationBuilder::append(FuseCode code, ::arm_compute::ICLTensor *ifm_alloc)
+{
+ switch (code)
+ {
+ case ANEURALNETWORKS_FUSED_NONE:
+ {
+ // DO NOTHING
+ break;
+ }
+ case ANEURALNETWORKS_FUSED_RELU:
+ {
+ appendReLU(ifm_alloc);
+ break;
+ }
+ default:
+ {
+ throw std::runtime_error("Not supported, yet");
+ }
+ }
+}
+
+//
+// StageGenerator
+//
+StageGenerator::StageGenerator(const neurun::graph::operand::Set &ctx,
+ const std::shared_ptr<TensorBuilder> &tensor_builder)
+ : _ctx(ctx), _tensor_builder(tensor_builder)
+{
+ // DO NOTHING
+}
+
+Stage StageGenerator::generate(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
+ const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
+ const auto ker_shape = _ctx.at(ker_index).shape().asKernel();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ assert((ANEURALNETWORKS_PADDING_SAME == padding_type) ||
+ (ANEURALNETWORKS_PADDING_VALID == padding_type));
+
+ ::internal::Stride stride;
+
+ stride.vertical = _ctx.at(vstride_index).asScalar<int32_t>();
+ stride.horizontal = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+ int ker_index;
+ int bias_index;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+ param.ker_index = ker_index.asInt();
+ param.bias_index = bias_index.asInt();
+
+ param.stride = stride;
+ param.padding =
+ (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(ifm_shape, ofm_shape, stride, ker_shape.W, ker_shape.H)
+ : ::internal::valid_padding();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get();
+ auto ker_alloc = tensors->at(::neurun::graph::operand::Index{param.ker_index}).get();
+ auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get();
+
+ const auto conv_info = asPadStringInfo(param.padding, param.stride);
+
+ std::unique_ptr<::arm_compute::CLConvolutionLayer> fn{new ::arm_compute::CLConvolutionLayer};
+
+ fn->configure(ifm_alloc, ker_alloc, bias_alloc, ofm_alloc, conv_info);
+
+ builder.append(std::move(fn));
+
+ ActivationBuilder{builder}.append(param.activation, ofm_alloc);
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::MaxPool2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+
+ const ::neurun::graph::operand::Index kh_index{node.param().kh_index};
+ const ::neurun::graph::operand::Index kw_index{node.param().kw_index};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+
+ const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
+ const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
+
+ const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>();
+ const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>();
+
+ const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>();
+ const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+
+ uint32_t kw;
+ uint32_t kh;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ // TODO Add 'activation' field
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+
+ param.kh = kh;
+ param.kw = kw;
+
+ param.stride.vertical = vstride;
+ param.stride.horizontal = hstride;
+
+ param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(ifm_shape, ofm_shape, param.stride, kw, kh)
+ : ::internal::valid_padding();
+
+ VERBOSE(MaxPool2D) << "IFM_H: " << ifm_shape.H << std::endl;
+ VERBOSE(MaxPool2D) << "IFM_W: " << ifm_shape.W << std::endl;
+ VERBOSE(MaxPool2D) << "OFM_H: " << ofm_shape.H << std::endl;
+ VERBOSE(MaxPool2D) << "OFM_W: " << ofm_shape.W << std::endl;
+ VERBOSE(MaxPool2D) << "KER_H: " << kh << std::endl;
+ VERBOSE(MaxPool2D) << "KER_W: " << kw << std::endl;
+ VERBOSE(MaxPool2D) << "STRIDE_H: " << vstride << std::endl;
+ VERBOSE(MaxPool2D) << "STRIDE_W: " << hstride << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(T): " << param.padding.top << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(L): " << param.padding.left << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(R): " << param.padding.right << std::endl;
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get();
+
+ ::arm_compute::PoolingLayerInfo info{::arm_compute::PoolingType::MAX,
+ ::arm_compute::Size2D{param.kw, param.kh},
+ asPadStringInfo(param.padding, param.stride)};
+
+ std::unique_ptr<::arm_compute::CLPoolingLayer> fn{new ::arm_compute::CLPoolingLayer};
+
+ fn->configure(ifm_alloc, ofm_alloc, info);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::AvgPool2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+
+ const ::neurun::graph::operand::Index kh_index{node.param().kh_index};
+ const ::neurun::graph::operand::Index kw_index{node.param().kw_index};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+
+ const auto ofm_shape = _ctx.at(ofm_index).shape().asFeature();
+ const auto ifm_shape = _ctx.at(ifm_index).shape().asFeature();
+
+ const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>();
+ const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>();
+
+ const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>();
+ const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ assert((ANEURALNETWORKS_PADDING_SAME == padding_type) ||
+ (ANEURALNETWORKS_PADDING_VALID == padding_type));
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+
+ uint32_t kw;
+ uint32_t kh;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ // TODO Add 'activation' field
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+
+ param.kh = kh;
+ param.kw = kw;
+
+ param.stride.vertical = vstride;
+ param.stride.horizontal = hstride;
+
+ param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(ifm_shape, ofm_shape, param.stride, kw, kh)
+ : ::internal::valid_padding();
+
+ VERBOSE(AvgPool2D) << "IFM_H: " << ifm_shape.H << std::endl;
+ VERBOSE(AvgPool2D) << "IFM_W: " << ifm_shape.W << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_H: " << ofm_shape.H << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_W: " << ofm_shape.W << std::endl;
+ VERBOSE(AvgPool2D) << "KER_H: " << kh << std::endl;
+ VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl;
+ VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(R): " << param.padding.right << std::endl;
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get();
+
+ ::arm_compute::PoolingLayerInfo info{
+ ::arm_compute::PoolingType::AVG, ::arm_compute::Size2D{param.kw, param.kh},
+ asPadStringInfo(param.padding, param.stride), true /* exclude_padding */};
+
+ std::unique_ptr<::arm_compute::CLPoolingLayer> fn{new ::arm_compute::CLPoolingLayer};
+
+ fn->configure(ifm_alloc, ofm_alloc, info);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Concat::Node &node)
+{
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index axis_index{node.param().axis_index};
+
+ struct Param
+ {
+ int32_t output_index;
+ std::vector<int32_t> input_indexes;
+
+ int32_t axis;
+ };
+
+ Param param;
+
+ param.output_index = ofm_index.asInt();
+ for (const auto &e : node.getInputs())
+ {
+ param.input_indexes.emplace_back(e.asInt());
+ }
+ param.axis = _ctx.at(axis_index).asScalar<int32_t>();
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+
+ std::vector<::arm_compute::ICLTensor *> input_allocs;
+ for (auto ifm_ind : param.input_indexes)
+ {
+ input_allocs.emplace_back(tensors->at(::neurun::graph::operand::Index{ifm_ind}).get());
+ }
+
+ std::unique_ptr<::neurun::kernel::acl_cl::ConcatLayer> fn{
+ new ::neurun::kernel::acl_cl::ConcatLayer};
+
+ fn->configure(input_allocs, param.axis, output_alloc);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::FullyConnected::Node &node)
+{
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ // Construct operation parameters
+ struct Param
+ {
+ int output_index;
+
+ int input_index;
+ int weight_index;
+ int bias_index;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+ param.weight_index = weight_index.asInt();
+ param.bias_index = bias_index.asInt();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+ auto weight_alloc = tensors->at(::neurun::graph::operand::Index{param.weight_index}).get();
+ auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get();
+
+ auto fn = make_layer<::arm_compute::CLFullyConnectedLayer>();
+
+ fn->configure(input_alloc, weight_alloc, bias_alloc, output_alloc);
+
+ builder.append(std::move(fn));
+
+ ActivationBuilder{builder}.append(param.activation, output_alloc);
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Reshape::Node &node)
+{
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+
+ struct Param
+ {
+ int output_index;
+ int input_index;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+
+ auto fn = make_layer<::arm_compute::CLReshapeLayer>();
+
+ fn->configure(input_alloc, output_alloc);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Softmax::Node &node)
+{
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index scale_index{node.param().scale_index};
+
+ assert(_ctx.at(scale_index).shape().rank() == 0);
+
+ struct Param
+ {
+ int output_index;
+ int input_index;
+ float scale;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+ param.scale = _ctx.at(scale_index).asScalar<float>();
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+
+ auto fn = make_layer<::arm_compute::CLSoftmaxLayer>();
+
+ fn->configure(input_alloc, output_alloc, param.scale);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::NOP::Node & /* node */)
+{
+ // DO NOTHING
+ return nullptr;
+}
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/acl_cl/StageGenerator.h b/runtimes/neurun/src/backend/acl_cl/StageGenerator.h
new file mode 100644
index 000000000..921604649
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/StageGenerator.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__
+#define __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__
+
+#include "backend/IStageGenerator.h"
+
+#include "graph/operand/Set.h"
+#include "backend/acl_cl/TensorBuilder.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+class StageGenerator : public IStageGenerator
+{
+public:
+ StageGenerator(const neurun::graph::operand::Set &ctx,
+ const std::shared_ptr<TensorBuilder> &tensor_builder);
+
+ virtual std::shared_ptr<ITensorBuilder> tensor_builder() override { return _tensor_builder; }
+
+ virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::Concat::Node &node) override;
+ virtual Stage generate(const graph::operation::FullyConnected::Node &node) override;
+ virtual Stage generate(const graph::operation::Reshape::Node &node) override;
+ virtual Stage generate(const graph::operation::Softmax::Node &node) override;
+ virtual Stage generate(const graph::operation::NOP::Node &node) override;
+
+private:
+ const neurun::graph::operand::Set &_ctx;
+ std::shared_ptr<TensorBuilder> _tensor_builder;
+};
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_ACL_CL_STAGE_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc
new file mode 100644
index 000000000..05943c26a
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend/acl_cl/TensorBuilder.h"
+
+#include <cassert>
+
+#include "operand/Object.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+TensorBuilder::TensorBuilder()
+{
+ // DO NOTHING
+}
+
+void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind)
+{
+ assert(_tensors.size() == 0);
+
+ _inds.insert(ind);
+}
+
+void TensorBuilder::prepare(codegen::Plan &plan,
+ const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx)
+{
+ assert(_tensors.size() == 0);
+
+ // TODO Handle SubTensor(subsumption)
+ // Currently this TensorBuilder does not have subsumption info yet
+
+ for (auto ind_int : _inds)
+ {
+ ::neurun::graph::operand::Index ind{ind_int};
+ auto tensor = std::make_shared<::arm_compute::CLTensor>();
+ tensor->allocator()->init(tensor_info_ctx.at(ind.asInt()));
+ plan.operands().set(ind, std::make_shared<operand::Object>(tensor));
+ _tensors[ind] = tensor;
+ }
+}
+
+void TensorBuilder::allocate(void)
+{
+ assert(_inds.size() == _tensors.size());
+
+ for (const auto &tensor_entry : _tensors)
+ {
+ auto tensor = tensor_entry.second;
+ tensor->allocator()->allocate();
+ }
+}
+
+std::shared_ptr<::arm_compute::CLTensor>
+TensorBuilder::at(const ::neurun::graph::operand::Index &ind)
+{
+ return _tensors.at(ind);
+}
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h
new file mode 100644
index 000000000..0a0f4e9ca
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/TensorBuilder.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__
+#define __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__
+
+#include "backend/ITensorBuilder.h"
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <arm_compute/runtime/CL/CLTensor.h>
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+
+class Plan;
+
+class TensorBuilder : public ITensorBuilder
+{
+public:
+ TensorBuilder();
+
+ virtual void mark(const ::neurun::graph::operand::Index &ind) override;
+ virtual void prepare(codegen::Plan &plan,
+ const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) override;
+ virtual void allocate(void) override;
+
+ std::shared_ptr<::arm_compute::CLTensor> at(const ::neurun::graph::operand::Index &ind);
+
+private:
+ std::unordered_set<graph::operand::Index> _inds;
+ std::unordered_map<graph::operand::Index, std::shared_ptr<::arm_compute::CLTensor>> _tensors;
+};
+
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_ACL_CL_TENSOR_BUILDER_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/feature/View.h b/runtimes/neurun/src/backend/acl_cl/feature/View.h
new file mode 100644
index 000000000..12025ce01
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/feature/View.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
+#define __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
+
+#include "util/feature/Reader.h"
+
+#include <arm_compute/core/ITensor.h>
+
+#include <cassert>
+
+namespace internal
+{
+namespace arm_compute
+{
+namespace feature
+{
+
+template <typename T> class View;
+
+template <> class View<float> final : public nnfw::util::feature::Reader<float>
+{
+public:
+ View(::arm_compute::ITensor *tensor) : _tensor{tensor}
+ {
+ assert(tensor->info()->data_type() == ::arm_compute::DataType::F32);
+
+ // TODO Validate whether tensor is a feature map, or not
+
+ _shape.C = tensor->info()->dimension(2);
+ _shape.H = tensor->info()->dimension(1);
+ _shape.W = tensor->info()->dimension(0);
+ }
+
+public:
+ const ::nnfw::util::feature::Shape &shape(void) const { return _shape; }
+
+public:
+ float at(uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ const auto offset = feature_index_to_byte_offset(ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+ float at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ const auto offset = feature_index_to_byte_offset(batch, ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+public:
+ float &at(uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto offset = feature_index_to_byte_offset(ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+ float &at(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto offset = feature_index_to_byte_offset(batch, ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+private:
+ size_t feature_index_to_byte_offset(uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ // ARM Compute uses CHW ordering
+ return _tensor->info()->offset_element_in_bytes(::arm_compute::Coordinates{col, row, ch});
+ }
+ size_t feature_index_to_byte_offset(uint32_t batch, uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ // ARM Compute uses CHW ordering
+ return _tensor->info()->offset_element_in_bytes(
+ ::arm_compute::Coordinates{col, row, ch, batch});
+ }
+
+private:
+ ::nnfw::util::feature::Shape _shape;
+ ::arm_compute::ITensor *_tensor;
+};
+
+} // namespace feature
+} // namespace arm_compute
+} // namespace internal
+
+#endif // __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/kernel/View.h b/runtimes/neurun/src/backend/acl_cl/kernel/View.h
new file mode 100644
index 000000000..aec9a8892
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/kernel/View.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_ARM_COMPUTE_KERNEL_VIEW_H__
+#define __INTERNAL_ARM_COMPUTE_KERNEL_VIEW_H__
+
+#include "util/kernel/Shape.h"
+#include "util/kernel/Reader.h"
+
+#include <arm_compute/core/ITensor.h>
+
+#include <cassert>
+
+namespace internal
+{
+namespace arm_compute
+{
+namespace kernel
+{
+
+template <typename T> class View;
+
+template <> class View<float> final : public nnfw::util::kernel::Reader<float>
+{
+public:
+ View(::arm_compute::ITensor *tensor) : _tensor{tensor}
+ {
+ assert(tensor->info()->data_type() == ::arm_compute::DataType::F32);
+
+ _shape.N = tensor->info()->dimension(3);
+ _shape.C = tensor->info()->dimension(2);
+ _shape.H = tensor->info()->dimension(1);
+ _shape.W = tensor->info()->dimension(0);
+ }
+
+public:
+ const ::nnfw::util::kernel::Shape &shape(void) const { return _shape; }
+
+public:
+ float at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const override
+ {
+ const auto offset = kernel_index_to_byte_offset(nth, ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+public:
+ float &at(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col)
+ {
+ const auto offset = kernel_index_to_byte_offset(nth, ch, row, col);
+
+ float *ptr = reinterpret_cast<float *>(_tensor->buffer() + offset);
+
+ return *ptr;
+ }
+
+private:
+ size_t kernel_index_to_byte_offset(uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) const
+ {
+ return _tensor->info()->offset_element_in_bytes(::arm_compute::Coordinates{col, row, ch, nth});
+ }
+
+private:
+ ::nnfw::util::kernel::Shape _shape;
+ ::arm_compute::ITensor *_tensor;
+};
+
+} // namespace kernel
+} // namespace arm_compute
+} // namespace internal
+
+#endif // __INTERNAL_ARM_COMPUTE_FEATURE_VIEW_H__
diff --git a/runtimes/neurun/src/backend/acl_cl/operand/Object.cc b/runtimes/neurun/src/backend/acl_cl/operand/Object.cc
new file mode 100644
index 000000000..98b96a11a
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/operand/Object.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Object.h"
+
+#include <arm_compute/runtime/CL/CLScheduler.h>
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+namespace operand
+{
+
+void Object::access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const
+{
+ auto &queue = ::arm_compute::CLScheduler::get().queue();
+
+ _tensor->map(queue);
+ fn(*_tensor);
+ _tensor->unmap(queue);
+}
+
+} // namespace operand
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/acl_cl/operand/Object.h b/runtimes/neurun/src/backend/acl_cl/operand/Object.h
new file mode 100644
index 000000000..da33c0549
--- /dev/null
+++ b/runtimes/neurun/src/backend/acl_cl/operand/Object.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__
+#define __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__
+
+#include <memory>
+#include <arm_compute/core/CL/ICLTensor.h>
+
+#include "backend/IObject.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace acl_cl
+{
+namespace operand
+{
+
+class Object : public backend::operand::IObject
+{
+public:
+ Object() = default;
+
+public:
+ Object(const std::shared_ptr<::arm_compute::ICLTensor> &tensor) : _tensor{tensor}
+ {
+ // DO NOTHING
+ }
+
+public:
+ ::arm_compute::ICLTensor *ptr(void) const override { return _tensor.get(); }
+
+private:
+ std::shared_ptr<::arm_compute::ICLTensor> _tensor;
+
+public:
+ void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const override;
+};
+
+} // namespace operand
+} // namespace acl_cl
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_ACL_CL_OPERAND_OBJECT_H__
diff --git a/runtimes/neurun/src/backend/cpu/BackendConfig.cc b/runtimes/neurun/src/backend/cpu/BackendConfig.cc
new file mode 100644
index 000000000..34fc3491a
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/BackendConfig.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "backend/cpu/BackendConfig.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+void BackendConfig::initialize()
+{
+ // DO NOTHING
+}
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/cpu/BackendConfig.h b/runtimes/neurun/src/backend/cpu/BackendConfig.h
new file mode 100644
index 000000000..109235bb1
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/BackendConfig.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__
+#define __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__
+
+#include "backend/IBackendConfig.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+class BackendConfig : public IBackendConfig
+{
+public:
+ BackendConfig()
+ {
+ // DO NOTHING
+ }
+
+ virtual void initialize() override;
+ virtual graph::operand::Layout getOperandLayout() { return graph::operand::Layout::NHWC; }
+};
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_BACKEND_CONFIG_H__
diff --git a/runtimes/neurun/src/backend/cpu/CMakeLists.txt b/runtimes/neurun/src/backend/cpu/CMakeLists.txt
new file mode 100644
index 000000000..95e9af687
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/CMakeLists.txt
@@ -0,0 +1,19 @@
+file(GLOB_RECURSE SOURCES "*.cc")
+
+add_library(${LIB_NEURUN_BACKEND_CPU} STATIC ${SOURCES})
+
+target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${NNFW_INCLUDE_DIR})
+target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${NEURUN_INCLUDE_DIR})
+target_include_directories(${LIB_NEURUN_BACKEND_CPU} PUBLIC ${CMAKE_SOURCE_DIR}/externals/tensorflow)
+
+target_link_libraries(${LIB_NEURUN_BACKEND_CPU} arm_compute) # TODO We should not need this
+target_link_libraries(${LIB_NEURUN_BACKEND_CPU} tensorflow-lite)
+target_link_libraries(${LIB_NEURUN_BACKEND_CPU} nnfw_util)
+target_link_libraries(${LIB_NEURUN_BACKEND_CPU} nnfw_support_nnapi)
+target_link_libraries(${LIB_NEURUN_BACKEND_CPU} ${LIB_NEURUN_KERNEL_CPU})
+
+target_compile_options(${LIB_NEURUN_BACKEND_CPU} PRIVATE -Wall -Wextra -Werror)
+
+set_target_properties(${LIB_NEURUN_BACKEND_CPU} PROPERTIES POSITION_INDEPENDENT_CODE ON)
+set_target_properties(${LIB_NEURUN_BACKEND_CPU} PROPERTIES OUTPUT_NAME backend_cpu)
+install(TARGETS ${LIB_NEURUN_BACKEND_CPU} DESTINATION lib/neurun)
diff --git a/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc b/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc
new file mode 100644
index 000000000..7b08c7131
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/InitializerGenerator.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InitializerGenerator.h"
+
+#include "internal/nnapi/kernel/Reader.h"
+#include "internal/nnapi/kernel/View.h"
+#include "util/kernel/IndexIterator.h"
+
+#include "NeuralNetworks.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+InitializerGenerator::InitializerGenerator(const neurun::graph::operand::Set &ctx) : _ctx(ctx)
+{
+ // DO NOTHING
+}
+
+Initializer
+InitializerGenerator::generateWeight(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)};
+
+ const auto ker_shape = _ctx.at(ker_index).shape().asKernel();
+ auto ker_base = _ctx.at(ker_index).data().base();
+ auto ker_size = _ctx.at(ker_index).data().size();
+
+ return [ker_shape, ker_base, ker_size](::arm_compute::ITensor &tensor) {
+ const ::internal::nnapi::kernel::Reader<float> from{ker_shape, ker_base, ker_size};
+ ::internal::nnapi::kernel::View<float> into{&tensor};
+
+ ::nnfw::util::kernel::iterate(ker_shape)
+ << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+ const auto value = from.at(nth, ch, row, col);
+ into.at(nth, row, col, ch) = value;
+ };
+ };
+}
+
+Initializer InitializerGenerator::generateWeight(const graph::operation::FullyConnected::Node &node)
+{
+ const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+
+ const auto num_output = _ctx.at(weight_index).shape().dim(0);
+ auto weight_base = _ctx.at(weight_index).data().base();
+ auto weight_size = _ctx.at(weight_index).data().size();
+ auto weight_type = _ctx.at(weight_index).typeInfo().type();
+
+ // NOTE We assume that input is a feature map
+ // TODO Remove this restriction!
+ const auto ifm_shape = _ctx.at(input_index).shape().asFeature();
+
+ switch (weight_type)
+ {
+ case ::neurun::graph::operand::DataType::TENSOR_FLOAT32:
+ {
+ return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) {
+ const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H,
+ ifm_shape.W};
+ const ::internal::nnapi::kernel::Reader<float> from{ker_shape, weight_base, weight_size};
+
+ ::nnfw::util::kernel::iterate(ker_shape)
+ << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+ const auto value = from.at(nth, ch, row, col);
+
+ uint32_t offset = 0;
+
+ // NNAPI uses NHWC ordering
+ offset += nth * ifm_shape.H * ifm_shape.W * ifm_shape.C;
+ offset += row * ifm_shape.W * ifm_shape.C;
+ offset += col * ifm_shape.C;
+ offset += ch;
+
+ const ::arm_compute::Coordinates coordinate{offset};
+
+ auto into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ *into = value;
+ };
+ };
+ }
+ case ::neurun::graph::operand::DataType::TENSOR_QUANT8_ASYMM:
+ {
+ return [num_output, ifm_shape, weight_base, weight_size](::arm_compute::ITensor &tensor) {
+ const ::nnfw::util::kernel::Shape ker_shape{num_output, ifm_shape.C, ifm_shape.H,
+ ifm_shape.W};
+ const ::internal::nnapi::kernel::Reader<uint8_t> from{ker_shape, weight_base, weight_size};
+ ::nnfw::util::kernel::iterate(ker_shape)
+ << [&](uint32_t nth, uint32_t ch, uint32_t row, uint32_t col) {
+ const auto value = from.at(nth, ch, row, col);
+ uint32_t offset = 0;
+
+ // NNAPI uses NHWC ordering
+ offset += nth * ifm_shape.H * ifm_shape.W * ifm_shape.C;
+ offset += row * ifm_shape.W * ifm_shape.C;
+ offset += col * ifm_shape.C;
+ offset += ch;
+
+ const ::arm_compute::Coordinates coordinate{offset};
+
+ auto into = reinterpret_cast<uint8_t *>(tensor.ptr_to_element(coordinate));
+
+ *into = value;
+ };
+ };
+ }
+ default:
+ {
+ throw std::runtime_error("Not supported weight type");
+ }
+ }
+}
+
+Initializer InitializerGenerator::generateBias(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ // TODO Refactor so we can reuse the common code
+
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ auto bias_base = _ctx.at(bias_index).data().base();
+ const auto bias_size = _ctx.at(bias_index).shape().asVector();
+
+ return [bias_base, bias_size](::arm_compute::ITensor &tensor) {
+ for (int32_t n = 0; n < bias_size; ++n)
+ {
+ const ::arm_compute::Coordinates coordinate{n};
+
+ float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ const float *from = reinterpret_cast<const float *>(bias_base) + n;
+ const auto value = *from;
+
+ *into = value;
+ }
+ };
+}
+
+Initializer InitializerGenerator::generateBias(const graph::operation::FullyConnected::Node &node)
+{
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ auto bias_base = _ctx.at(bias_index).data().base();
+ auto bias_type = _ctx.at(bias_index).typeInfo().type();
+ const auto bias_size = _ctx.at(bias_index).shape().asVector();
+
+ switch (bias_type)
+ {
+ case ::neurun::graph::operand::DataType::TENSOR_FLOAT32:
+ {
+ return [bias_base, bias_size](::arm_compute::ITensor &tensor) {
+ for (int32_t n = 0; n < bias_size; ++n)
+ {
+ const ::arm_compute::Coordinates coordinate{n};
+
+ float *into = reinterpret_cast<float *>(tensor.ptr_to_element(coordinate));
+
+ const float *from = reinterpret_cast<const float *>(bias_base) + n;
+ const auto value = *from;
+
+ *into = value;
+ }
+ };
+ }
+ case ::neurun::graph::operand::DataType::TENSOR_QUANT8_ASYMM:
+ {
+ return [bias_base, bias_size](::arm_compute::ITensor &tensor) {
+ for (int32_t n = 0; n < bias_size; ++n)
+ {
+ const ::arm_compute::Coordinates coordinate{n};
+
+ uint8_t *into = reinterpret_cast<uint8_t *>(tensor.ptr_to_element(coordinate));
+
+ const uint8_t *from = reinterpret_cast<const uint8_t *>(bias_base) + n;
+ const auto value = *from;
+
+ *into = value;
+ }
+ };
+ }
+ default:
+ {
+ throw std::runtime_error("Not supported bias type");
+ }
+ }
+}
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/cpu/InitializerGenerator.h b/runtimes/neurun/src/backend/cpu/InitializerGenerator.h
new file mode 100644
index 000000000..42d37f48b
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/InitializerGenerator.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__
+#define __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__
+
+#include "backend/IInitializerGenerator.h"
+
+#include "graph/operand/Set.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+class InitializerGenerator : public IInitializerGenerator
+{
+public:
+ InitializerGenerator(const neurun::graph::operand::Set &ctx);
+
+ Initializer generateWeight(const graph::operation::Conv2D::Implicit::Node &node) override;
+ Initializer generateWeight(const graph::operation::FullyConnected::Node &node) override;
+
+ Initializer generateBias(const graph::operation::Conv2D::Implicit::Node &node) override;
+ Initializer generateBias(const graph::operation::FullyConnected::Node &node) override;
+
+private:
+ const neurun::graph::operand::Set &_ctx;
+};
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_INITIALIZER_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc b/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc
new file mode 100644
index 000000000..13d2a7ffc
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/MemoryAllocator.cc
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#include "internal/cpu/MemoryAllocator.h"
diff --git a/runtimes/neurun/src/backend/cpu/MemoryAllocator.h b/runtimes/neurun/src/backend/cpu/MemoryAllocator.h
new file mode 100644
index 000000000..e3550ac07
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/MemoryAllocator.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INTERNAL_CPU_MEMORY_ALLOCATOR_H__
+#define __INTERNAL_CPU_MEMORY_ALLOCATOR_H__
+
+#include "arm_compute/runtime/ITensorAllocator.h"
+#include "arm_compute/runtime/Memory.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+namespace arm_compute
+{
+class Coordinates;
+class TensorInfo;
+class Tensor;
+};
+
+/** Basic implementation of a CPU memory tensor allocator. */
+class TensorAllocator : public ITensorAllocator
+{
+public:
+ /** Default constructor. */
+ TensorAllocator(Tensor *owner = nullptr);
+ /** Default destructor */
+ ~TensorAllocator();
+
+ /** Make ITensorAllocator's init methods available */
+ using ITensorAllocator::init;
+
+ /** Shares the same backing memory with another tensor allocator, while the tensor info might be
+ * different.
+ * In other words this can be used to create a sub-tensor from another tensor while sharing the
+ * same memory.
+ *
+ * @note TensorAllocator have to be of the same specialized type.
+ *
+ * @param[in] allocator The allocator that owns the backing memory to be shared. Ownership becomes
+ * shared afterwards.
+ * @param[in] coords The starting coordinates of the new tensor inside the parent tensor.
+ * @param[in] sub_info The new tensor information (e.g. shape etc)
+ */
+ void init(const TensorAllocator &allocator, const Coordinates &coords, TensorInfo sub_info);
+
+ /** Returns the pointer to the allocated data. */
+ uint8_t *data() const;
+
+ /** Allocate size specified by TensorInfo of CPU memory.
+ *
+ * @note The tensor must not already be allocated when calling this function.
+ *
+ */
+ void allocate() override;
+
+ /** Free allocated CPU memory.
+ *
+ * @note The tensor must have been allocated when calling this function.
+ *
+ */
+ void free() override;
+ /** Import an existing memory as a tensor's backing memory
+ *
+ * @warning If the tensor is flagged to be managed by a memory manager,
+ * this call will lead to an error.
+ * @warning Ownership of memory depends on the way the @ref Memory object was constructed
+ * @note Calling free on a tensor with imported memory will just clear
+ * the internal pointer value.
+ *
+ * @param[in] memory Memory to import
+ *
+ * @return error status
+ */
+ arm_compute::Status import_memory(Memory memory);
+ /** Associates the tensor with a memory group
+ *
+ * @param[in] associated_memory_group Memory group to associate the tensor with
+ */
+ void set_associated_memory_group(MemoryGroup *associated_memory_group);
+
+protected:
+ /** No-op for CPU memory
+ *
+ * @return A pointer to the beginning of the tensor's allocation.
+ */
+ uint8_t *lock() override;
+
+ /** No-op for CPU memory. */
+ void unlock() override;
+
+private:
+ MemoryGroup *_associated_memory_group; /**< Registered memory manager */
+ Memory _memory; /**< CPU memory */
+ Tensor *_owner; /**< Owner of the allocator */
+};
+
+namespace internal
+{
+namespace cpu
+{
+
+class MemoryAllocator : public
+{
+};
+
+} // namespace cpu
+} // namespace internal
+
+#endif // __INTERNAL_CPU_MEMORY_ALLOCATOR_H__
diff --git a/runtimes/neurun/src/backend/cpu/StageGenerator.cc b/runtimes/neurun/src/backend/cpu/StageGenerator.cc
new file mode 100644
index 000000000..b7a3fa24a
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/StageGenerator.cc
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "StageGenerator.h"
+
+#include <stdexcept>
+
+#include "internal/Padding.h"
+#include "kernel/cpu/OperationUtils.h"
+#include "kernel/cpu/ConvolutionLayer.h"
+#include "kernel/cpu/AvgPoolLayer.h"
+#include "kernel/cpu/MaxPoolLayer.h"
+#include "kernel/cpu/ConcatLayer.h"
+#include "kernel/cpu/FullyConnectedLayer.h"
+#include "kernel/cpu/ReshapeLayer.h"
+#include "kernel/cpu/SoftMaxLayer.h"
+
+#include "logging.h"
+
+#include "support/nnapi/Utils.h"
+
+#include "logging.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+StageGenerator::StageGenerator(const neurun::graph::operand::Set &operand_ctx,
+ const std::shared_ptr<TensorBuilder> &tensor_builder)
+ : _ctx(operand_ctx), _tensor_builder(tensor_builder)
+{
+ // DO NOTHING
+}
+
+Stage StageGenerator::generate(const graph::operation::Conv2D::Implicit::Node &node)
+{
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index ker_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ assert((ANEURALNETWORKS_PADDING_SAME == padding_type) ||
+ (ANEURALNETWORKS_PADDING_VALID == padding_type));
+
+ ::internal::Stride stride;
+
+ stride.vertical = _ctx.at(vstride_index).asScalar<int32_t>();
+ stride.horizontal = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+ int ker_index;
+ int bias_index;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+ ::neurun::kernel::cpu::Shape ker_shape;
+ ::neurun::kernel::cpu::Shape bias_shape;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+ param.ker_index = ker_index.asInt();
+ param.bias_index = bias_index.asInt();
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index));
+ param.ker_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ker_index));
+ param.bias_shape = ::neurun::kernel::cpu::getShape(_ctx.at(bias_index));
+
+ param.stride = stride;
+ param.padding = (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(),
+ _ctx.at(ofm_index).shape().asFeature(), stride,
+ _ctx.at(ker_index).shape().asKernel().W,
+ _ctx.at(ker_index).shape().asKernel().H)
+ : ::internal::valid_padding();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index});
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index});
+ auto ker_alloc = tensors->at(::neurun::graph::operand::Index{param.ker_index});
+ auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index});
+
+ std::unique_ptr<::neurun::kernel::cpu::ConvolutionLayer> fn{
+ new ::neurun::kernel::cpu::ConvolutionLayer};
+
+ fn->configure(ifm_alloc->buffer(), param.ifm_shape, ker_alloc->buffer(), param.ker_shape,
+ bias_alloc->buffer(), param.bias_shape, param.padding.left, param.padding.right,
+ param.padding.top, param.padding.bottom, param.stride.horizontal,
+ param.stride.vertical, param.activation, ofm_alloc->buffer(), param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::MaxPool2D::Implicit::Node &node)
+{
+ VERBOSE(MaxPool2D) << "generate CPU MaxPool2D" << std::endl;
+
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+
+ const ::neurun::graph::operand::Index kh_index{node.param().kh_index};
+ const ::neurun::graph::operand::Index kw_index{node.param().kw_index};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>();
+ const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>();
+
+ const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>();
+ const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+
+ uint32_t kw;
+ uint32_t kh;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+
+ param.kh = kh;
+ param.kw = kw;
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index));
+
+ param.stride.vertical = vstride;
+ param.stride.horizontal = hstride;
+
+ param.padding =
+ (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(),
+ _ctx.at(ofm_index).shape().asFeature(), param.stride, kw, kh)
+ : ::internal::valid_padding();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ VERBOSE(MaxPool2D) << "IFM_H: " << _ctx.at(ifm_index).shape().asFeature().H << std::endl;
+ VERBOSE(MaxPool2D) << "IFM_W: " << _ctx.at(ifm_index).shape().asFeature().W << std::endl;
+ VERBOSE(MaxPool2D) << "OFM_H: " << _ctx.at(ofm_index).shape().asFeature().H << std::endl;
+ VERBOSE(MaxPool2D) << "OFM_W: " << _ctx.at(ofm_index).shape().asFeature().W << std::endl;
+ VERBOSE(MaxPool2D) << "KER_H: " << kh << std::endl;
+ VERBOSE(MaxPool2D) << "KER_W: " << kw << std::endl;
+ VERBOSE(MaxPool2D) << "STRIDE_H: " << vstride << std::endl;
+ VERBOSE(MaxPool2D) << "STRIDE_W: " << hstride << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(T): " << param.padding.top << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(L): " << param.padding.left << std::endl;
+ VERBOSE(MaxPool2D) << "PAD(R): " << param.padding.right << std::endl;
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get();
+
+ std::unique_ptr<::neurun::kernel::cpu::MaxPoolLayer> fn{
+ new ::neurun::kernel::cpu::MaxPoolLayer};
+
+ fn->configure(ifm_alloc->buffer(), param.ifm_shape, param.padding.left, param.padding.right,
+ param.padding.top, param.padding.bottom, param.stride.horizontal,
+ param.stride.vertical, param.kw, param.kh, param.activation, ofm_alloc->buffer(),
+ param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::AvgPool2D::Implicit::Node &node)
+{
+ VERBOSE(AvgPool2D) << "generate CPU AvgPool2D" << std::endl;
+
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index ifm_index{node.getInputs().at(0)};
+
+ const ::neurun::graph::operand::Index kh_index{node.param().kh_index};
+ const ::neurun::graph::operand::Index kw_index{node.param().kw_index};
+
+ const ::neurun::graph::operand::Index vstride_index{node.param().vstride_index};
+ const ::neurun::graph::operand::Index hstride_index{node.param().hstride_index};
+
+ const ::neurun::graph::operand::Index padding_index{node.param().padding_index};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ const int32_t kh = _ctx.at(kh_index).asScalar<int32_t>();
+ const int32_t kw = _ctx.at(kw_index).asScalar<int32_t>();
+
+ const int32_t vstride = _ctx.at(vstride_index).asScalar<int32_t>();
+ const int32_t hstride = _ctx.at(hstride_index).asScalar<int32_t>();
+
+ const PaddingCode padding_type =
+ static_cast<PaddingCode>(_ctx.at(padding_index).asScalar<int32_t>());
+
+ assert((ANEURALNETWORKS_PADDING_SAME == padding_type) ||
+ (ANEURALNETWORKS_PADDING_VALID == padding_type));
+
+ // Construct operation parameters
+ struct Param
+ {
+ int ofm_index;
+ int ifm_index;
+
+ uint32_t kw;
+ uint32_t kh;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+
+ ::internal::Padding padding;
+ ::internal::Stride stride;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.ofm_index = ofm_index.asInt();
+ param.ifm_index = ifm_index.asInt();
+
+ param.kh = kh;
+ param.kw = kw;
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ifm_index));
+
+ param.stride.vertical = vstride;
+ param.stride.horizontal = hstride;
+
+ param.padding =
+ (padding_type == ANEURALNETWORKS_PADDING_SAME)
+ ? ::internal::same_padding(_ctx.at(ifm_index).shape().asFeature(),
+ _ctx.at(ofm_index).shape().asFeature(), param.stride, kw, kh)
+ : ::internal::valid_padding();
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ VERBOSE(AvgPool2D) << "IFM_H: " << _ctx.at(ifm_index).shape().asFeature().H << std::endl;
+ VERBOSE(AvgPool2D) << "IFM_W: " << _ctx.at(ifm_index).shape().asFeature().W << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_H: " << _ctx.at(ofm_index).shape().asFeature().H << std::endl;
+ VERBOSE(AvgPool2D) << "OFM_W: " << _ctx.at(ofm_index).shape().asFeature().W << std::endl;
+ VERBOSE(AvgPool2D) << "KER_H: " << kh << std::endl;
+ VERBOSE(AvgPool2D) << "KER_W: " << kw << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_H: " << vstride << std::endl;
+ VERBOSE(AvgPool2D) << "STRIDE_W: " << hstride << std::endl;
+ VERBOSE(AvgPool2D) << "PAD: " << ::nnfw::support::nnapi::to_string(padding_type) << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(T): " << param.padding.top << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(B): " << param.padding.bottom << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(L): " << param.padding.left << std::endl;
+ VERBOSE(AvgPool2D) << "PAD(R): " << param.padding.right << std::endl;
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto ofm_alloc = tensors->at(::neurun::graph::operand::Index{param.ofm_index}).get();
+ auto ifm_alloc = tensors->at(::neurun::graph::operand::Index{param.ifm_index}).get();
+
+ std::unique_ptr<::neurun::kernel::cpu::AvgPoolLayer> fn{
+ new ::neurun::kernel::cpu::AvgPoolLayer};
+
+ fn->configure(ifm_alloc->buffer(), param.ifm_shape, param.padding.left, param.padding.right,
+ param.padding.top, param.padding.bottom, param.stride.horizontal,
+ param.stride.vertical, param.kw, param.kh, param.activation, ofm_alloc->buffer(),
+ param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Concat::Node &node)
+{
+ VERBOSE(Concat) << "generate CPU Concat" << std::endl;
+
+ const ::neurun::graph::operand::Index ofm_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index axis_index{node.param().axis_index};
+
+ struct Param
+ {
+ int32_t output_index;
+ std::vector<int32_t> input_indexes;
+
+ int32_t axis;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ std::vector<::neurun::kernel::cpu::Shape> ifm_shapes;
+ };
+
+ Param param;
+
+ param.output_index = ofm_index.asInt();
+ for (const auto &e : node.getInputs())
+ {
+ param.input_indexes.emplace_back(e.asInt());
+ }
+ param.axis = _ctx.at(axis_index).asScalar<int32_t>();
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(ofm_index));
+
+ for (auto e : node.getInputs())
+ {
+ param.ifm_shapes.emplace_back(::neurun::kernel::cpu::getShape(_ctx.at(e)));
+ }
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+
+ std::vector<const uint8_t *> input_buffers;
+ for (auto ifm_ind : param.input_indexes)
+ {
+ input_buffers.emplace_back(
+ tensors->at(::neurun::graph::operand::Index{ifm_ind}).get()->buffer());
+ }
+
+ std::unique_ptr<::neurun::kernel::cpu::ConcatLayer> fn{new ::neurun::kernel::cpu::ConcatLayer};
+
+ fn->configure(input_buffers, param.ifm_shapes, param.axis, output_alloc->buffer(),
+ param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::FullyConnected::Node &node)
+{
+ VERBOSE(FullyConnected) << "generate CPU FullyConnected" << std::endl;
+
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index weight_index{node.getInputs().at(1)};
+ const ::neurun::graph::operand::Index bias_index{node.getInputs().at(2)};
+ const ::neurun::graph::operand::Index activation_index{node.param().activation_index};
+
+ // Construct operation parameters
+ struct Param
+ {
+ int output_index;
+ int input_index;
+ int weight_index;
+ int bias_index;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+ ::neurun::kernel::cpu::Shape weight_shape;
+ ::neurun::kernel::cpu::Shape bias_shape;
+
+ FuseCode activation;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+ param.weight_index = weight_index.asInt();
+ param.bias_index = bias_index.asInt();
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index));
+ param.weight_shape = ::neurun::kernel::cpu::getShape(_ctx.at(weight_index));
+ param.bias_shape = ::neurun::kernel::cpu::getShape(_ctx.at(bias_index));
+
+ param.activation = static_cast<FuseCode>(_ctx.at(activation_index).asScalar<int32_t>());
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+ auto weight_alloc = tensors->at(::neurun::graph::operand::Index{param.weight_index}).get();
+ auto bias_alloc = tensors->at(::neurun::graph::operand::Index{param.bias_index}).get();
+
+ std::unique_ptr<::neurun::kernel::cpu::FullyConnectedLayer> fn{
+ new ::neurun::kernel::cpu::FullyConnectedLayer};
+
+ fn->configure(input_alloc->buffer(), param.ifm_shape, weight_alloc->buffer(),
+ param.weight_shape, bias_alloc->buffer(), param.bias_shape, param.activation,
+ output_alloc->buffer(), param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Reshape::Node &node)
+{
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+
+ struct Param
+ {
+ int output_index;
+ int input_index;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index));
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+
+ std::unique_ptr<::neurun::kernel::cpu::ReshapeLayer> fn{
+ new ::neurun::kernel::cpu::ReshapeLayer};
+
+ fn->configure(input_alloc->buffer(), param.ifm_shape, output_alloc->buffer(), param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::Softmax::Node &node)
+{
+ VERBOSE(Softmax) << "generate CPU Softmax" << std::endl;
+
+ const ::neurun::graph::operand::Index output_index{node.getOutputs().at(0)};
+ const ::neurun::graph::operand::Index input_index{node.getInputs().at(0)};
+ const ::neurun::graph::operand::Index scale_index{node.param().scale_index};
+
+ struct Param
+ {
+ int output_index;
+ int input_index;
+
+ ::neurun::kernel::cpu::Shape ofm_shape;
+ ::neurun::kernel::cpu::Shape ifm_shape;
+
+ float scale;
+ };
+
+ Param param;
+
+ param.output_index = output_index.asInt();
+ param.input_index = input_index.asInt();
+
+ param.ofm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(output_index));
+ param.ifm_shape = ::neurun::kernel::cpu::getShape(_ctx.at(input_index));
+
+ param.scale = _ctx.at(scale_index).asScalar<float>();
+
+ auto tensors = _tensor_builder;
+
+ return [tensors, param](IExecutionBuilder &builder) {
+ auto output_alloc = tensors->at(::neurun::graph::operand::Index{param.output_index}).get();
+ auto input_alloc = tensors->at(::neurun::graph::operand::Index{param.input_index}).get();
+
+ std::unique_ptr<::neurun::kernel::cpu::SoftMaxLayer> fn{
+ new ::neurun::kernel::cpu::SoftMaxLayer};
+
+ fn->configure(input_alloc->buffer(), param.ifm_shape, param.scale, output_alloc->buffer(),
+ param.ofm_shape);
+
+ builder.append(std::move(fn));
+ };
+}
+
+Stage StageGenerator::generate(const graph::operation::NOP::Node & /* node */)
+{
+ // DO NOTHING
+ return nullptr;
+}
+
+} // namespace neurun
+} // namespace backend
+} // namespace cpu
diff --git a/runtimes/neurun/src/backend/cpu/StageGenerator.h b/runtimes/neurun/src/backend/cpu/StageGenerator.h
new file mode 100644
index 000000000..acdd2c8b2
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/StageGenerator.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__
+#define __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__
+
+#include "backend/IStageGenerator.h"
+
+#include "graph/operand/Set.h"
+#include "backend/cpu/operand/Tensor.h"
+#include "TensorBuilder.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+class StageGenerator : public IStageGenerator
+{
+public:
+ StageGenerator(const neurun::graph::operand::Set &ctx,
+ const std::shared_ptr<TensorBuilder> &tensor_builder);
+
+ virtual std::shared_ptr<ITensorBuilder> tensor_builder() override { return _tensor_builder; }
+
+ virtual Stage generate(const graph::operation::Conv2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::MaxPool2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::AvgPool2D::Implicit::Node &node) override;
+ virtual Stage generate(const graph::operation::Concat::Node &node) override;
+ virtual Stage generate(const graph::operation::FullyConnected::Node &node) override;
+ virtual Stage generate(const graph::operation::Reshape::Node &node) override;
+ virtual Stage generate(const graph::operation::Softmax::Node &node) override;
+ virtual Stage generate(const graph::operation::NOP::Node &node) override;
+
+private:
+ const neurun::graph::operand::Set &_ctx;
+ std::shared_ptr<TensorBuilder> _tensor_builder;
+};
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_STAGE_GENERATOR_H__
diff --git a/runtimes/neurun/src/backend/cpu/TensorBuilder.cc b/runtimes/neurun/src/backend/cpu/TensorBuilder.cc
new file mode 100644
index 000000000..1b972a830
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/TensorBuilder.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TensorBuilder.h"
+
+#include <cassert>
+
+#include "operand/Object.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+TensorBuilder::TensorBuilder()
+{
+ // DO NOTHING
+}
+
+void TensorBuilder::mark(const ::neurun::graph::operand::Index &ind)
+{
+ assert(_tensors.size() == 0);
+
+ _inds.insert(ind);
+}
+
+void TensorBuilder::prepare(codegen::Plan &plan,
+ const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx)
+{
+ assert(_tensors.size() == 0);
+
+ for (auto ind_int : _inds)
+ {
+ ::neurun::graph::operand::Index ind{ind_int};
+ auto tensor = std::make_shared<operand::Tensor>(tensor_info_ctx.at(ind.asInt()));
+ // TODO Fix allocation here. When Tensor object is created the memory for tensor is also
+ // allocated, and this must be fixed.
+ plan.operands().set(ind, std::make_shared<operand::Object>(tensor));
+ _tensors[ind] = tensor;
+ }
+}
+
+void TensorBuilder::allocate(void)
+{
+ assert(_inds.size() == _tensors.size());
+
+ // NOTE For now nothing to do. Allocation is done in prepare stage, which is wrong
+ // See also: comment in `prepare()`
+}
+
+std::shared_ptr<operand::Tensor> TensorBuilder::at(const ::neurun::graph::operand::Index &ind)
+{
+ return _tensors.at(ind);
+}
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/cpu/TensorBuilder.h b/runtimes/neurun/src/backend/cpu/TensorBuilder.h
new file mode 100644
index 000000000..f61a930fe
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/TensorBuilder.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__
+#define __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "backend/ITensorBuilder.h"
+#include "backend/cpu/operand/Tensor.h"
+#include "graph/operand/Index.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+
+class Plan;
+
+class TensorBuilder : public ITensorBuilder
+{
+public:
+ TensorBuilder();
+
+ virtual void mark(const ::neurun::graph::operand::Index &ind) override;
+ virtual void prepare(codegen::Plan &plan,
+ const std::map<int, ::arm_compute::TensorInfo> &tensor_info_ctx) override;
+ virtual void allocate(void) override;
+
+ std::shared_ptr<operand::Tensor> at(const ::neurun::graph::operand::Index &ind);
+
+private:
+ std::unordered_set<graph::operand::Index> _inds;
+ std::unordered_map<graph::operand::Index, std::shared_ptr<operand::Tensor>> _tensors;
+};
+
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_TENSOR_BUILDER_H__
diff --git a/runtimes/neurun/src/backend/cpu/operand/Object.cc b/runtimes/neurun/src/backend/cpu/operand/Object.cc
new file mode 100644
index 000000000..52b63fba7
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/operand/Object.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Object.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+namespace operand
+{
+
+void Object::access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const
+{
+ fn(*_tensor);
+}
+
+} // namespace operand
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/cpu/operand/Object.h b/runtimes/neurun/src/backend/cpu/operand/Object.h
new file mode 100644
index 000000000..08f63f3dc
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/operand/Object.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__
+#define __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__
+
+#include <memory>
+#include <arm_compute/core/ITensor.h>
+
+#include "backend/IObject.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+namespace operand
+{
+
+class Object : public backend::operand::IObject
+{
+public:
+ Object() = default;
+
+public:
+ Object(const std::shared_ptr<::arm_compute::ITensor> &tensor) : _tensor{tensor}
+ {
+ // DO NOTHING
+ }
+
+public:
+ ::arm_compute::ITensor *ptr(void) const override { return _tensor.get(); }
+
+private:
+ std::shared_ptr<::arm_compute::ITensor> _tensor;
+
+public:
+ void access(const std::function<void(::arm_compute::ITensor &tensor)> &fn) const override;
+};
+
+} // namespace operand
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_OPERAND_OBJECT_H__
diff --git a/runtimes/neurun/src/backend/cpu/operand/Tensor.cc b/runtimes/neurun/src/backend/cpu/operand/Tensor.cc
new file mode 100644
index 000000000..0e4f34aac
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/operand/Tensor.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Tensor.h"
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+namespace operand
+{
+
+// NO IMPLEMENTATION YET
+
+} // namespace operand
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
diff --git a/runtimes/neurun/src/backend/cpu/operand/Tensor.h b/runtimes/neurun/src/backend/cpu/operand/Tensor.h
new file mode 100644
index 000000000..83a99acf2
--- /dev/null
+++ b/runtimes/neurun/src/backend/cpu/operand/Tensor.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__
+#define __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__
+
+#include <arm_compute/core/ITensor.h>
+#include <arm_compute/core/TensorInfo.h>
+
+namespace neurun
+{
+namespace backend
+{
+namespace cpu
+{
+namespace operand
+{
+
+class Tensor : public ::arm_compute::ITensor
+{
+public:
+ Tensor() = default;
+
+ Tensor(::arm_compute::TensorInfo info) : _info(info)
+ {
+ // TODO Do not allocate buffer here. This tensor is just an abstract Tensor object for cpu.
+ uint32_t size = _info.total_size(); // NOTE This size may not be accurate
+ _buffer = new uint8_t[size]; // NOTE The allocated buffer is never deallocated.
+ }
+
+ Tensor(uint8_t *buffer) : _buffer(buffer)
+ {
+ // DO NOTHING
+ }
+
+public:
+ void setBuffer(uint8_t *buffer) { _buffer = buffer; }
+
+public:
+ ::arm_compute::TensorInfo *info() const override
+ {
+ return const_cast<::arm_compute::TensorInfo *>(&_info);
+ }
+
+ ::arm_compute::TensorInfo *info() override { return &_info; }
+
+ uint8_t *buffer() const override { return _buffer; }
+
+private:
+ ::arm_compute::TensorInfo _info;
+ uint8_t *_buffer = nullptr;
+};
+
+} // namespace operand
+} // namespace cpu
+} // namespace backend
+} // namespace neurun
+
+#endif // __NEURUN_BACKEND_CPU_OPERAND_TENSOR_H__