summaryrefslogtreecommitdiff
path: root/compiler/enco/core
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/enco/core')
-rw-r--r--compiler/enco/core/CMakeLists.txt35
-rw-r--r--compiler/enco/core/include/enco/Backend.h41
-rw-r--r--compiler/enco/core/src/ANN/Binder.h219
-rw-r--r--compiler/enco/core/src/ANN/Context.cpp31
-rw-r--r--compiler/enco/core/src/ANN/Context.h57
-rw-r--r--compiler/enco/core/src/ANN/Context.test.cpp73
-rw-r--r--compiler/enco/core/src/ANN/IR/DType.cpp25
-rw-r--r--compiler/enco/core/src/ANN/IR/DType.h36
-rw-r--r--compiler/enco/core/src/ANN/IR/DType.test.cpp25
-rw-r--r--compiler/enco/core/src/ANN/IR/InputList.h31
-rw-r--r--compiler/enco/core/src/ANN/IR/Module.h60
-rw-r--r--compiler/enco/core/src/ANN/IR/Module.test.cpp36
-rw-r--r--compiler/enco/core/src/ANN/IR/Operand.h82
-rw-r--r--compiler/enco/core/src/ANN/IR/Operand.test.cpp37
-rw-r--r--compiler/enco/core/src/ANN/IR/OperandID.h48
-rw-r--r--compiler/enco/core/src/ANN/IR/OperandID.test.cpp33
-rw-r--r--compiler/enco/core/src/ANN/IR/OperandInventory.cpp57
-rw-r--r--compiler/enco/core/src/ANN/IR/OperandInventory.h56
-rw-r--r--compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp30
-rw-r--r--compiler/enco/core/src/ANN/IR/Operation.def17
-rw-r--r--compiler/enco/core/src/ANN/IR/Operation.h59
-rw-r--r--compiler/enco/core/src/ANN/IR/Operation.test.cpp28
-rw-r--r--compiler/enco/core/src/ANN/IR/OperationInventory.cpp32
-rw-r--r--compiler/enco/core/src/ANN/IR/OperationInventory.h48
-rw-r--r--compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp40
-rw-r--r--compiler/enco/core/src/ANN/IR/OutputList.h31
-rw-r--r--compiler/enco/core/src/ANN/IR/Weight.h70
-rw-r--r--compiler/enco/core/src/ANN/IR/Weight.test.cpp53
-rw-r--r--compiler/enco/core/src/ANN/IR/WeightInventory.cpp34
-rw-r--r--compiler/enco/core/src/ANN/IR/WeightInventory.h38
-rw-r--r--compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp29
-rw-r--r--compiler/enco/core/src/AsmCode.cpp33
-rw-r--r--compiler/enco/core/src/AsmCode.h51
-rw-r--r--compiler/enco/core/src/Backend.cpp178
-rw-r--r--compiler/enco/core/src/Code.h47
-rw-r--r--compiler/enco/core/src/Code.test.cpp30
-rw-r--r--compiler/enco/core/src/CodeIndex.h76
-rw-r--r--compiler/enco/core/src/CppCode.cpp553
-rw-r--r--compiler/enco/core/src/CppCode.h51
-rw-r--r--compiler/enco/core/src/CppGen/Host.cpp306
-rw-r--r--compiler/enco/core/src/CppGen/Host.h48
-rw-r--r--compiler/enco/core/src/CppGen/MemoryContext.cpp40
-rw-r--r--compiler/enco/core/src/CppGen/MemoryContext.h55
-rw-r--r--compiler/enco/core/src/CppGen/Subnet.cpp422
-rw-r--r--compiler/enco/core/src/CppGen/Subnet.h91
-rw-r--r--compiler/enco/core/src/Dims.h34
-rw-r--r--compiler/enco/core/src/IRUtils.cpp65
-rw-r--r--compiler/enco/core/src/IRUtils.h41
-rw-r--r--compiler/enco/core/src/IRValidator.cpp85
-rw-r--r--compiler/enco/core/src/IRValidator.h29
-rw-r--r--compiler/enco/core/src/IRValidator.test.cpp130
-rw-r--r--compiler/enco/core/src/Pass.h78
-rw-r--r--compiler/enco/core/src/Pass.test.cpp41
-rw-r--r--compiler/enco/core/src/Pipeline.h46
-rw-r--r--compiler/enco/core/src/Pipeline.test.cpp26
-rw-r--r--compiler/enco/core/src/Session.cpp58
-rw-r--r--compiler/enco/core/src/Session.h45
-rw-r--r--compiler/enco/core/src/String.h57
-rw-r--r--compiler/enco/core/src/Support/Debugging.cpp533
-rw-r--r--compiler/enco/core/src/Support/Debugging.h110
-rw-r--r--compiler/enco/core/src/Support/Debugging.test.cpp26
-rw-r--r--compiler/enco/core/src/Transforms/AvgPoolLowering.cpp229
-rw-r--r--compiler/enco/core/src/Transforms/AvgPoolLowering.h43
-rw-r--r--compiler/enco/core/src/Transforms/ConcatLowering.cpp196
-rw-r--r--compiler/enco/core/src/Transforms/ConcatLowering.h43
-rw-r--r--compiler/enco/core/src/Transforms/ConstantFolding.cpp442
-rw-r--r--compiler/enco/core/src/Transforms/ConstantFolding.h43
-rw-r--r--compiler/enco/core/src/Transforms/ConstantFolding.test.cpp327
-rw-r--r--compiler/enco/core/src/Transforms/CopyLowering.cpp105
-rw-r--r--compiler/enco/core/src/Transforms/CopyLowering.h43
-rw-r--r--compiler/enco/core/src/Transforms/DataLayoutConversion.cpp383
-rw-r--r--compiler/enco/core/src/Transforms/DataLayoutConversion.h43
-rw-r--r--compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp33
-rw-r--r--compiler/enco/core/src/Transforms/DeadBagElimination.cpp72
-rw-r--r--compiler/enco/core/src/Transforms/DeadBagElimination.h48
-rw-r--r--compiler/enco/core/src/Transforms/DeadObjectElimination.cpp77
-rw-r--r--compiler/enco/core/src/Transforms/DeadObjectElimination.h47
-rw-r--r--compiler/enco/core/src/Transforms/Duplicate.cpp135
-rw-r--r--compiler/enco/core/src/Transforms/Duplicate.h43
-rw-r--r--compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp119
-rw-r--r--compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h73
-rw-r--r--compiler/enco/core/src/Transforms/FeatureUnification.cpp216
-rw-r--r--compiler/enco/core/src/Transforms/FeatureUnification.h68
-rw-r--r--compiler/enco/core/src/Transforms/FreeInstrElimination.cpp65
-rw-r--r--compiler/enco/core/src/Transforms/FreeInstrElimination.h54
-rw-r--r--compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp34
-rw-r--r--compiler/enco/core/src/Transforms/FreeOpElimination.cpp59
-rw-r--r--compiler/enco/core/src/Transforms/FreeOpElimination.h54
-rw-r--r--compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp34
-rw-r--r--compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp181
-rw-r--r--compiler/enco/core/src/Transforms/GlobalDataGeneration.h54
-rw-r--r--compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp139
-rw-r--r--compiler/enco/core/src/Transforms/IdenticalObjectReduction.h69
-rw-r--r--compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp32
-rw-r--r--compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp84
-rw-r--r--compiler/enco/core/src/Transforms/IndirectCopyElimination.h60
-rw-r--r--compiler/enco/core/src/Transforms/IntrinsicSelection.cpp100
-rw-r--r--compiler/enco/core/src/Transforms/IntrinsicSelection.h47
-rw-r--r--compiler/enco/core/src/Transforms/Optimizations.cpp257
-rw-r--r--compiler/enco/core/src/Transforms/Optimizations.h123
-rw-r--r--compiler/enco/core/src/Transforms/Split.cpp1233
-rw-r--r--compiler/enco/core/src/Transforms/Split.h48
-rw-r--r--compiler/enco/core/src/Usage.cpp58
-rw-r--r--compiler/enco/core/src/Usage.h34
-rw-r--r--compiler/enco/core/src/coex/IR.h109
-rw-r--r--compiler/enco/core/src/coex/IR.test.cpp38
106 files changed, 10670 insertions, 0 deletions
diff --git a/compiler/enco/core/CMakeLists.txt b/compiler/enco/core/CMakeLists.txt
new file mode 100644
index 000000000..f437e687a
--- /dev/null
+++ b/compiler/enco/core/CMakeLists.txt
@@ -0,0 +1,35 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+###
+### enco_core is built as a shared library to support "interactive debugging".
+###
+### interactive debugging helpers are stripped during linking when enco_core is
+### built as a static library
+###
+add_library(enco_core SHARED ${SOURCES})
+target_include_directories(enco_core PRIVATE src)
+target_include_directories(enco_core PUBLIC include)
+target_link_libraries(enco_core PUBLIC enco_intf_cmdline)
+target_link_libraries(enco_core PUBLIC coco_core)
+target_link_libraries(enco_core PUBLIC coco_generic)
+# These libraries are linked for internal use, and thus does not appear in public headers.
+target_link_libraries(enco_core PRIVATE pp)
+target_link_libraries(enco_core PRIVATE morph)
+target_link_libraries(enco_core PRIVATE stdex)
+# Let's use nncc project-wide build options
+target_link_libraries(enco_core PRIVATE nncc_common)
+
+nnas_find_package(GTest QUIET)
+
+if(NOT GTest_FOUND)
+ return()
+endif(NOT GTest_FOUND)
+
+add_executable(enco_core_test ${TESTS})
+target_include_directories(enco_core_test PRIVATE src)
+target_link_libraries(enco_core_test gtest_main)
+target_link_libraries(enco_core_test enco_core)
+target_link_libraries(enco_core_test morph)
+add_test(enco_core_test enco_core_test)
diff --git a/compiler/enco/core/include/enco/Backend.h b/compiler/enco/core/include/enco/Backend.h
new file mode 100644
index 000000000..5da903ed2
--- /dev/null
+++ b/compiler/enco/core/include/enco/Backend.h
@@ -0,0 +1,41 @@
+/*
+ * 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 __ENCO_BACKEND_H__
+#define __ENCO_BACKEND_H__
+
+#include "cmdline/View.h"
+
+#include "coco/IR/Module.h"
+#include "coco/IR/Data.h"
+
+#include <memory>
+
+namespace enco
+{
+
+struct Backend
+{
+ virtual ~Backend() = default;
+
+ virtual void compile(coco::Module *m, coco::Data *d) = 0;
+};
+
+} // namespace enco
+
+std::unique_ptr<enco::Backend> make_backend(const cmdline::View &);
+
+#endif // __ENCO_BACKEND_H__
diff --git a/compiler/enco/core/src/ANN/Binder.h b/compiler/enco/core/src/ANN/Binder.h
new file mode 100644
index 000000000..71b95676b
--- /dev/null
+++ b/compiler/enco/core/src/ANN/Binder.h
@@ -0,0 +1,219 @@
+/*
+ * 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 __ANN_BINDER_H__
+#define __ANN_BINDER_H__
+
+#include "ANN/IR/Module.h"
+
+#include <coco/IR.h>
+
+#include <morph/nnapi.h>
+
+#include <type_traits>
+
+/**
+ * @brief A bridge between ann::Module and coco::Block
+ */
+class ANNBinder
+{
+public:
+ ANNBinder(coco::Block *block, std::unique_ptr<ann::Module> &&module)
+ : _block{block}, _module{std::move(module)}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const coco::Block *block(void) const { return _block; }
+ coco::Block *block(void) { return _block; }
+
+public:
+ const ann::Module *module(void) const { return _module.get(); }
+
+public:
+ /**
+ * @brief Return the set of bags that the current ANN subnet accesses
+ */
+ std::set<coco::Bag *> bags(void) const
+ {
+ std::set<coco::Bag *> res;
+
+ for (auto it = _operands.begin(); it != _operands.end(); ++it)
+ {
+ res.insert(it->first);
+ }
+
+ return res;
+ }
+
+public:
+ template <typename T> ann::OperandID addOperand(void)
+ {
+ return _module->operand()->create(ann::dtype<T>());
+ };
+
+ template <typename T> ann::OperandID addOperand(const nncc::core::ADT::tensor::Shape &shape)
+ {
+ return _module->operand()->create(ann::dtype<T>(), shape);
+ }
+
+public:
+ template <typename T> ann::OperandID addOperand(const coco::FeatureObject *obj)
+ {
+ auto bag = obj->bag();
+ assert(bag != nullptr);
+
+ auto it = _operands.find(bag);
+
+ if (it != _operands.end())
+ {
+ return it->second;
+ }
+
+ auto operand = addOperand<T>(morph::nnapi::as_tensor_shape(obj->shape()));
+ _operands[obj->bag()] = operand;
+ return operand;
+ };
+
+ template <typename T> ann::OperandID addOperand(const coco::KernelObject *obj)
+ {
+ auto bag = obj->bag();
+ assert(bag != nullptr);
+
+ auto it = _operands.find(bag);
+
+ if (it != _operands.end())
+ {
+ return it->second;
+ }
+
+ auto operand = addOperand<T>(morph::nnapi::as_tensor_shape(obj->shape()));
+ _operands[obj->bag()] = operand;
+ return operand;
+ };
+
+public:
+ /// @brief Set scalar weight
+ template <typename T> void setOperand(const ann::OperandID &id, const T &value)
+ {
+ static_assert(std::is_arithmetic<T>::value, "T should be arithmetic");
+ auto weight = _module->weight()->create();
+ weight->fill(value);
+ _module->operand()->at(id)->weight(weight);
+ }
+
+ /// @brief Set non-scalar weight
+ template <typename It> void setOperand(const ann::OperandID &id, It beg, It end)
+ {
+ auto weight = _module->weight()->create();
+ weight->fill(beg, end);
+ _module->operand()->at(id)->weight(weight);
+ }
+
+public:
+ void addOperation(ann::Operation::Code code, std::initializer_list<ann::OperandID> inputs,
+ std::initializer_list<ann::OperandID> outputs)
+ {
+ _module->operation()->create(code, inputs, outputs);
+ }
+
+public:
+ /**
+ * @brief Identify a sequence of coco::Bag * as subnet's inputs
+ *
+ * NOTE 1. This method takes input iterator over coco::Bag * values
+ * NOTE 2. All the identifyInputs class except the last one will be ignored if there are
+ * multiple identifyInputs calls
+ */
+ template <typename It> void identifyInputs(It beg, It end)
+ {
+ _inputs.clear();
+ _module->input()->clear();
+
+ for (auto it = beg; it != end; ++it)
+ {
+ auto const bag = *it;
+ _inputs.emplace_back(*it);
+ _module->input()->emplace_back(_operands.at(bag));
+ }
+ }
+
+ template <typename T> void identifyInputs(T &&values)
+ {
+ identifyInputs(std::begin(values), std::end(values));
+ }
+
+public:
+ /**
+ * @brief Identify a sequence of coco::Bag * as subnet's outputs
+ *
+ * NOTE 1. This method takes input iterator over coco::Bag * values
+ * NOTE 2. All the identifyOutputs class except the last one will be ignored if there are
+ * multiple identifyOutputs calls
+ */
+ template <typename It> void identifyOutputs(It beg, It end)
+ {
+ _outputs.clear();
+ _module->output()->clear();
+
+ for (auto it = beg; it != end; ++it)
+ {
+ auto const bag = *it;
+ _outputs.emplace_back(bag);
+ _module->output()->emplace_back(_operands.at(bag));
+ }
+ }
+
+ template <typename T> void identifyOutputs(T &&values)
+ {
+ identifyOutputs(std::begin(values), std::end(values));
+ }
+
+public:
+ coco::Bag *input(uint32_t n) const { return _inputs.at(n); }
+ coco::Bag *output(uint32_t n) const { return _outputs.at(n); }
+
+public:
+ /**
+ * @brief Return true if a given bag has an associated operand in ANN IR
+ */
+ bool associated(coco::Bag *b) const { return _operands.find(b) != _operands.end(); }
+
+ /**
+ * @brief Return operand ID associated with a given bag
+ * @note The behavior of operand(b) is defined only when associated(b) holds.
+ */
+ ann::OperandID operand(coco::Bag *b) const
+ {
+ assert(associated(b));
+ return _operands.at(b);
+ }
+
+private:
+ coco::Block *const _block;
+ std::unique_ptr<ann::Module> _module;
+
+private:
+ std::vector<coco::Bag *> _inputs;
+ std::vector<coco::Bag *> _outputs;
+
+private:
+ /// @brief Operand ID assigned for each coco::Bag
+ std::map<coco::Bag *, ann::OperandID> _operands;
+};
+
+#endif // __ANN_BINDER_H__
diff --git a/compiler/enco/core/src/ANN/Context.cpp b/compiler/enco/core/src/ANN/Context.cpp
new file mode 100644
index 000000000..d4d1882fa
--- /dev/null
+++ b/compiler/enco/core/src/ANN/Context.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "ANN/Context.h"
+
+#include <stdex/Memory.h>
+
+ANNBinder *ANNContext::create(coco::Block *blk)
+{
+ auto mod = stdex::make_unique<ann::Module>();
+ auto obj = stdex::make_unique<ANNBinder>(blk, std::move(mod));
+ auto ptr = obj.get();
+
+ _binders.emplace_back(std::move(obj));
+ _map[blk] = ptr;
+
+ return ptr;
+}
diff --git a/compiler/enco/core/src/ANN/Context.h b/compiler/enco/core/src/ANN/Context.h
new file mode 100644
index 000000000..915651eb5
--- /dev/null
+++ b/compiler/enco/core/src/ANN/Context.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 __ANN_CONTEXT_H__
+#define __ANN_CONTEXT_H__
+
+#include "ANN/Binder.h"
+
+#include <map>
+#include <vector>
+
+#include <memory>
+
+struct ANNContext
+{
+public:
+ ANNBinder *create(coco::Block *blk);
+
+public:
+ uint32_t count(void) const { return _binders.size(); }
+
+public:
+ ANNBinder *nth(uint32_t n) { return _binders.at(n).get(); }
+ const ANNBinder *nth(uint32_t n) const { return _binders.at(n).get(); }
+
+public:
+ ANNBinder *find(const coco::Block *blk) const
+ {
+ auto it = _map.find(blk);
+
+ if (it == _map.end())
+ {
+ return nullptr;
+ }
+
+ return it->second;
+ }
+
+private:
+ std::vector<std::unique_ptr<ANNBinder>> _binders;
+ std::map<const coco::Block *, ANNBinder *> _map;
+};
+
+#endif // __ANN_CONTEXT_H__
diff --git a/compiler/enco/core/src/ANN/Context.test.cpp b/compiler/enco/core/src/ANN/Context.test.cpp
new file mode 100644
index 000000000..7fd26f30c
--- /dev/null
+++ b/compiler/enco/core/src/ANN/Context.test.cpp
@@ -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 "Context.h"
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+class ANNContextTest : public ::testing::Test
+{
+public:
+ ANNContextTest() { m = coco::Module::create(); }
+
+public:
+ virtual ~ANNContextTest() = default;
+
+protected:
+ std::unique_ptr<coco::Module> m;
+};
+}
+
+TEST_F(ANNContextTest, constructor)
+{
+ ANNContext ann_ctx;
+
+ ASSERT_EQ(ann_ctx.count(), 0);
+}
+
+TEST_F(ANNContextTest, create)
+{
+ ANNContext ann_ctx;
+
+ auto blk = m->entity()->block()->create();
+ auto binder = ann_ctx.create(blk);
+
+ ASSERT_NE(binder, nullptr);
+}
+
+TEST_F(ANNContextTest, find)
+{
+ ANNContext ann_ctx;
+
+ // CASE: Corresponding binder does not exist
+ {
+ auto blk = m->entity()->block()->create();
+ ASSERT_EQ(ann_ctx.find(blk), nullptr);
+ }
+
+ // CASE: Corresponding binder does exist
+ {
+ auto blk = m->entity()->block()->create();
+ auto binder_created = ann_ctx.create(blk);
+ auto binder_found = ann_ctx.find(blk);
+
+ ASSERT_EQ(binder_created, binder_found);
+ }
+}
diff --git a/compiler/enco/core/src/ANN/IR/DType.cpp b/compiler/enco/core/src/ANN/IR/DType.cpp
new file mode 100644
index 000000000..7d4585a49
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/DType.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "DType.h"
+
+namespace ann
+{
+
+template <> DType dtype<int32_t>(void) { return DType::S32; }
+template <> DType dtype<float>(void) { return DType::F32; }
+
+} // namespace ann
diff --git a/compiler/enco/core/src/ANN/IR/DType.h b/compiler/enco/core/src/ANN/IR/DType.h
new file mode 100644
index 000000000..b7583b09a
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/DType.h
@@ -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.
+ */
+
+#ifndef __ANN_IR_DTYPE_H__
+#define __ANN_IR_DTYPE_H__
+
+#include <cstdint>
+
+namespace ann
+{
+
+enum class DType
+{
+ UNK,
+ S32,
+ F32
+};
+
+template <typename T> DType dtype(void);
+
+} // namespace ann
+
+#endif // __ANN_IR_DTYPE_H__
diff --git a/compiler/enco/core/src/ANN/IR/DType.test.cpp b/compiler/enco/core/src/ANN/IR/DType.test.cpp
new file mode 100644
index 000000000..8184ece9b
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/DType.test.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "DType.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_DTYPE, dtype)
+{
+ ASSERT_EQ(ann::dtype<int>(), ann::DType::S32);
+ ASSERT_EQ(ann::dtype<float>(), ann::DType::F32);
+}
diff --git a/compiler/enco/core/src/ANN/IR/InputList.h b/compiler/enco/core/src/ANN/IR/InputList.h
new file mode 100644
index 000000000..51f0fd95a
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/InputList.h
@@ -0,0 +1,31 @@
+/*
+ * 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 __ANN_IR_INPUT_LIST_H__
+#define __ANN_IR_INPUT_LIST_H__
+
+#include "ANN/IR/OperandID.h"
+
+#include <vector>
+
+namespace ann
+{
+
+using InputList = std::vector<OperandID>;
+
+} // namespace ann
+
+#endif // __ANN_IR_INPUT_LIST_H__
diff --git a/compiler/enco/core/src/ANN/IR/Module.h b/compiler/enco/core/src/ANN/IR/Module.h
new file mode 100644
index 000000000..b443b4235
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Module.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 __ANN_IR_MODULE_H__
+#define __ANN_IR_MODULE_H__
+
+#include "ANN/IR/WeightInventory.h"
+#include "ANN/IR/OperandInventory.h"
+#include "ANN/IR/OperationInventory.h"
+#include "ANN/IR/InputList.h"
+#include "ANN/IR/OutputList.h"
+
+namespace ann
+{
+
+class Module
+{
+public:
+ Module() = default;
+
+public:
+ WeightInventory *weight(void) { return &_weight; }
+ const WeightInventory *weight(void) const { return &_weight; }
+
+ OperandInventory *operand(void) { return &_operand; }
+ const OperandInventory *operand(void) const { return &_operand; }
+
+ OperationInventory *operation(void) { return &_operation; }
+ const OperationInventory *operation(void) const { return &_operation; }
+
+ InputList *input(void) { return &_input; }
+ const InputList *input(void) const { return &_input; }
+
+ OutputList *output(void) { return &_output; }
+ const OutputList *output(void) const { return &_output; }
+
+private:
+ WeightInventory _weight;
+ OperandInventory _operand;
+ OperationInventory _operation;
+ InputList _input;
+ OutputList _output;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_MODULE_H__
diff --git a/compiler/enco/core/src/ANN/IR/Module.test.cpp b/compiler/enco/core/src/ANN/IR/Module.test.cpp
new file mode 100644
index 000000000..4b946c875
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Module.test.cpp
@@ -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 "Module.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_MODULE, constructor)
+{
+ ann::Module m;
+
+ ann::Module *mutable_ptr = &m;
+ const ann::Module *immutable_ptr = &m;
+
+ ASSERT_NE(mutable_ptr->weight(), nullptr);
+ ASSERT_EQ(mutable_ptr->weight(), immutable_ptr->weight());
+
+ ASSERT_NE(mutable_ptr->operand(), nullptr);
+ ASSERT_EQ(mutable_ptr->operand(), immutable_ptr->operand());
+
+ ASSERT_NE(mutable_ptr->operation(), nullptr);
+ ASSERT_EQ(mutable_ptr->operation(), immutable_ptr->operation());
+}
diff --git a/compiler/enco/core/src/ANN/IR/Operand.h b/compiler/enco/core/src/ANN/IR/Operand.h
new file mode 100644
index 000000000..3b15ed739
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Operand.h
@@ -0,0 +1,82 @@
+/*
+ * 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 __ANN_IR_OPERAND_H__
+#define __ANN_IR_OPERAND_H__
+
+#include "ANN/IR/DType.h"
+#include "ANN/IR/Weight.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+
+namespace ann
+{
+
+class Operand
+{
+public:
+ virtual ~Operand() = default;
+
+public:
+ DType dtype(void) const { return _dtype; }
+ void dtype(const DType &dtype) { _dtype = dtype; }
+
+ const Weight *weight(void) const { return _weight; }
+ void weight(const Weight *weight) { _weight = weight; }
+
+private:
+ DType _dtype = DType::UNK;
+ const Weight *_weight = nullptr;
+};
+
+} // namespace ann
+
+namespace ann
+{
+
+/**
+ * @brief Plain (non-qunatized) Scalar Operand
+ */
+struct ScalarOperand final : public Operand
+{
+};
+
+} // namespace ann
+
+namespace ann
+{
+
+/**
+ * @brief Plain (non-qunatized) Tensor Operand
+ */
+struct TensorOperand final : public Operand
+{
+public:
+ TensorOperand(const nncc::core::ADT::tensor::Shape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const nncc::core::ADT::tensor::Shape &shape(void) const { return _shape; }
+
+private:
+ nncc::core::ADT::tensor::Shape _shape;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_OPERAND_H__
diff --git a/compiler/enco/core/src/ANN/IR/Operand.test.cpp b/compiler/enco/core/src/ANN/IR/Operand.test.cpp
new file mode 100644
index 000000000..98ac4ebd0
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Operand.test.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "Operand.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_SCALAR_OPERAND, constructor)
+{
+ const ann::ScalarOperand operand;
+
+ ASSERT_EQ(operand.dtype(), ann::DType::UNK);
+ ASSERT_EQ(operand.weight(), nullptr);
+}
+
+TEST(ANN_IR_TENSOR_OPERAND, constructor)
+{
+ const nncc::core::ADT::tensor::Shape shape{1, 2};
+ const ann::TensorOperand operand{shape};
+
+ ASSERT_EQ(operand.dtype(), ann::DType::UNK);
+ ASSERT_EQ(operand.weight(), nullptr);
+ ASSERT_EQ(operand.shape(), shape);
+}
diff --git a/compiler/enco/core/src/ANN/IR/OperandID.h b/compiler/enco/core/src/ANN/IR/OperandID.h
new file mode 100644
index 000000000..f1617aacb
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperandID.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __ANN_IR_OPERAND_ID_H__
+#define __ANN_IR_OPERAND_ID_H__
+
+#include <cstdint>
+
+namespace ann
+{
+
+class OperandID
+{
+public:
+ OperandID() : _value{0}
+ {
+ // DO NOTHING
+ }
+
+public:
+ explicit OperandID(uint32_t value) : _value{value}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t value(void) const { return _value; }
+
+private:
+ uint32_t _value;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_OPERAND_ID_H__
diff --git a/compiler/enco/core/src/ANN/IR/OperandID.test.cpp b/compiler/enco/core/src/ANN/IR/OperandID.test.cpp
new file mode 100644
index 000000000..04c23b9c8
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperandID.test.cpp
@@ -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 "OperandID.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_OPERAND_ID, default_constructor)
+{
+ ann::OperandID id;
+
+ ASSERT_EQ(id.value(), 0);
+}
+
+TEST(ANN_IR_OPERAND_ID, explicit_constructor)
+{
+ ann::OperandID id{4};
+
+ ASSERT_EQ(id.value(), 4);
+}
diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.cpp b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp
new file mode 100644
index 000000000..c7ad38811
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperandInventory.cpp
@@ -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.
+ */
+
+#include "ANN/IR/OperandInventory.h"
+
+#include <stdex/Memory.h>
+
+using stdex::make_unique;
+
+namespace ann
+{
+
+OperandID OperandInventory::create(const DType &dtype)
+{
+ uint32_t id = _operands.size();
+
+ auto operand = make_unique<ScalarOperand>();
+ operand->dtype(dtype);
+
+ _operands.emplace_back(std::move(operand));
+
+ return OperandID{id};
+}
+
+OperandID OperandInventory::create(const DType &dtype, const nncc::core::ADT::tensor::Shape &shape)
+{
+ uint32_t id = _operands.size();
+
+ auto operand = make_unique<TensorOperand>(shape);
+ operand->dtype(dtype);
+
+ _operands.emplace_back(std::move(operand));
+
+ return OperandID{id};
+}
+
+Operand *OperandInventory::at(const OperandID &id) { return _operands.at(id.value()).get(); }
+
+const Operand *OperandInventory::at(const OperandID &id) const
+{
+ return _operands.at(id.value()).get();
+}
+
+} // namespace ann
diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.h b/compiler/enco/core/src/ANN/IR/OperandInventory.h
new file mode 100644
index 000000000..23eb08119
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperandInventory.h
@@ -0,0 +1,56 @@
+/*
+ * 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 __ANN_IR_OPERAND_INVENTORY_H__
+#define __ANN_IR_OPERAND_INVENTORY_H__
+
+#include "ANN/IR/OperandID.h"
+#include "ANN/IR/Operand.h"
+
+#include <nncc/core/ADT/tensor/Shape.h>
+
+#include <memory>
+#include <vector>
+
+namespace ann
+{
+
+class OperandInventory
+{
+public:
+ OperandID create(const DType &);
+ OperandID create(const DType &, const nncc::core::ADT::tensor::Shape &);
+
+public:
+ template <typename Callable> void each(Callable &&cb) const
+ {
+ for (uint32_t n = 0; n < _operands.size(); ++n)
+ {
+ cb(OperandID{n}, _operands.at(n).get());
+ }
+ }
+
+public:
+ Operand *at(const OperandID &id);
+ const Operand *at(const OperandID &id) const;
+
+private:
+ std::vector<std::unique_ptr<Operand>> _operands;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_OPERAND_INVENTORY_H__
diff --git a/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp b/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp
new file mode 100644
index 000000000..e576752bc
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperandInventory.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "OperandInventory.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_OPERAND_INVENTORY, constructor)
+{
+ ann::OperandInventory inven;
+
+ uint32_t count = 0;
+
+ inven.each([&](const ann::OperandID &, const ann::Operand *) { ++count; });
+
+ ASSERT_EQ(count, 0);
+}
diff --git a/compiler/enco/core/src/ANN/IR/Operation.def b/compiler/enco/core/src/ANN/IR/Operation.def
new file mode 100644
index 000000000..68fd394cf
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Operation.def
@@ -0,0 +1,17 @@
+#ifndef ANN_OPERATION
+#error Define ANN_OPERATION first
+#endif // ANN_OPERATION
+
+// ANN_OPERATION(TAG, ENUM_VALUE)
+ANN_OPERATION(ADD, ANEURALNETWORKS_ADD)
+ANN_OPERATION(MUL, ANEURALNETWORKS_MUL)
+ANN_OPERATION(CONV_2D, ANEURALNETWORKS_CONV_2D)
+ANN_OPERATION(DEPTHWISE_CONV_2D, ANEURALNETWORKS_DEPTHWISE_CONV_2D)
+ANN_OPERATION(MAX_POOL_2D, ANEURALNETWORKS_MAX_POOL_2D)
+ANN_OPERATION(AVG_POOL_2D, ANEURALNETWORKS_AVERAGE_POOL_2D)
+ANN_OPERATION(RELU, ANEURALNETWORKS_RELU)
+ANN_OPERATION(RELU6, ANEURALNETWORKS_RELU6)
+ANN_OPERATION(PAD, ANEURALNETWORKS_PAD)
+ANN_OPERATION(CONCAT, ANEURALNETWORKS_CONCATENATION)
+ANN_OPERATION(SUB, ANEURALNETWORKS_SUB)
+ANN_OPERATION(DIV, ANEURALNETWORKS_DIV)
diff --git a/compiler/enco/core/src/ANN/IR/Operation.h b/compiler/enco/core/src/ANN/IR/Operation.h
new file mode 100644
index 000000000..cacc2b794
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Operation.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 __ANN_IR_OPERATION_H__
+#define __ANN_IR_OPERATION_H__
+
+#include "ANN/IR/OperandID.h"
+
+#include <initializer_list>
+#include <vector>
+
+namespace ann
+{
+
+class Operation
+{
+public:
+ enum class Code
+ {
+#define ANN_OPERATION(TAG, VALUE) TAG,
+#include "Operation.def"
+#undef ANN_OPERATION
+ };
+
+public:
+ Operation(const Code &code, std::initializer_list<OperandID> inputs,
+ std::initializer_list<OperandID> outputs)
+ : _code{code}, _inputs{inputs}, _outputs{outputs}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const Code &code(void) const { return _code; }
+ const std::vector<OperandID> &inputs(void) const { return _inputs; }
+ const std::vector<OperandID> &outputs(void) const { return _outputs; }
+
+private:
+ Code _code;
+ std::vector<OperandID> _inputs;
+ std::vector<OperandID> _outputs;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_OPERATION_H__
diff --git a/compiler/enco/core/src/ANN/IR/Operation.test.cpp b/compiler/enco/core/src/ANN/IR/Operation.test.cpp
new file mode 100644
index 000000000..d1b716733
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Operation.test.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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 "Operation.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_OPERATION, constructor)
+{
+ ann::Operation op{ann::Operation::Code::CONV_2D, {}, {}};
+
+ ASSERT_EQ(op.code(), ann::Operation::Code::CONV_2D);
+ ASSERT_EQ(op.inputs().size(), 0);
+ ASSERT_EQ(op.outputs().size(), 0);
+}
diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.cpp b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp
new file mode 100644
index 000000000..37d48c170
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperationInventory.cpp
@@ -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 "OperationInventory.h"
+
+#include <stdex/Memory.h>
+
+using stdex::make_unique;
+
+namespace ann
+{
+
+void OperationInventory::create(Operation::Code code, std::initializer_list<OperandID> inputs,
+ std::initializer_list<OperandID> outputs)
+{
+ _operations.emplace_back(make_unique<Operation>(code, inputs, outputs));
+}
+
+} // namespace ann
diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.h b/compiler/enco/core/src/ANN/IR/OperationInventory.h
new file mode 100644
index 000000000..11c6be98a
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperationInventory.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __ANN_IR_OPERATION_INVENTORY_H__
+#define __ANN_IR_OPERATION_INVENTORY_H__
+
+#include "ANN/IR/Operation.h"
+#include "ANN/IR/OperandID.h"
+
+#include <initializer_list>
+
+#include <memory>
+
+namespace ann
+{
+
+class OperationInventory
+{
+public:
+ void create(Operation::Code code, std::initializer_list<OperandID> inputs,
+ std::initializer_list<OperandID> outputs);
+
+public:
+ uint32_t count(void) const { return _operations.size(); }
+
+public:
+ const Operation *at(uint32_t n) const { return _operations.at(n).get(); }
+
+private:
+ std::vector<std::unique_ptr<Operation>> _operations;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_OPERATION_INVENTORY_H__
diff --git a/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp b/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp
new file mode 100644
index 000000000..0e91a4f53
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OperationInventory.test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "OperationInventory.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_OPERATION_INVENTORY, constructor)
+{
+ ann::OperationInventory inven;
+
+ ASSERT_EQ(inven.count(), 0);
+}
+
+TEST(ANN_IR_OPERATION_INVENTORY, create)
+{
+ ann::OperationInventory inven;
+
+ inven.create(ann::Operation::Code::CONV_2D, {ann::OperandID{0}}, {ann::OperandID{3}});
+
+ ASSERT_EQ(inven.count(), 1);
+ ASSERT_NE(inven.at(0), nullptr);
+
+ ASSERT_EQ(inven.at(0)->code(), ann::Operation::Code::CONV_2D);
+ ASSERT_EQ(inven.at(0)->inputs().size(), 1);
+ ASSERT_EQ(inven.at(0)->outputs().size(), 1);
+}
diff --git a/compiler/enco/core/src/ANN/IR/OutputList.h b/compiler/enco/core/src/ANN/IR/OutputList.h
new file mode 100644
index 000000000..2dd891138
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/OutputList.h
@@ -0,0 +1,31 @@
+/*
+ * 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 __ANN_IR_OUTPUT_LIST_H__
+#define __ANN_IR_OUTPUT_LIST_H__
+
+#include "ANN/IR/OperandID.h"
+
+#include <vector>
+
+namespace ann
+{
+
+using OutputList = std::vector<OperandID>;
+
+} // namespace ann
+
+#endif // __ANN_IR_OUTPUT_LIST_H__
diff --git a/compiler/enco/core/src/ANN/IR/Weight.h b/compiler/enco/core/src/ANN/IR/Weight.h
new file mode 100644
index 000000000..062aa6d19
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Weight.h
@@ -0,0 +1,70 @@
+/*
+ * 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 __ANN_IR_WEIGHT_H__
+#define __ANN_IR_WEIGHT_H__
+
+#include <vector>
+
+#include <cstdint>
+#include <type_traits>
+
+namespace ann
+{
+
+class Weight
+{
+public:
+ const uint8_t *base(void) const { return _buffer.data(); }
+ uint32_t size(void) const { return _buffer.size(); }
+
+public:
+ template <typename T> void fill(const T &value)
+ {
+ static_assert(std::is_arithmetic<T>::value, "T should be arithmetic");
+ _buffer.clear();
+
+ auto arr = reinterpret_cast<const uint8_t *>(&value);
+
+ for (uint32_t b = 0; b < sizeof(T); ++b)
+ {
+ _buffer.emplace_back(arr[b]);
+ }
+ }
+
+ template <typename It> void fill(It beg, It end)
+ {
+ _buffer.clear();
+
+ for (auto it = beg; it != end; ++it)
+ {
+ const auto value = *it;
+ auto arr = reinterpret_cast<const uint8_t *>(&value);
+
+ for (uint32_t b = 0; b < sizeof(value); ++b)
+ {
+ _buffer.emplace_back(arr[b]);
+ }
+ }
+ }
+
+private:
+ std::vector<uint8_t> _buffer;
+};
+
+} // namespace ann
+
+#endif // __ANN_IR_WEIGHT_H__
diff --git a/compiler/enco/core/src/ANN/IR/Weight.test.cpp b/compiler/enco/core/src/ANN/IR/Weight.test.cpp
new file mode 100644
index 000000000..53532114c
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/Weight.test.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "Weight.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_WEIGHT, constructor)
+{
+ ann::Weight weight;
+
+ ASSERT_EQ(weight.base(), nullptr);
+ ASSERT_EQ(weight.size(), 0);
+}
+
+TEST(ANN_IR_WEIGHT, fill_scalar_int)
+{
+ ann::Weight weight;
+
+ weight.fill(3);
+
+ ASSERT_NE(weight.base(), nullptr);
+ ASSERT_EQ(*reinterpret_cast<const int *>(weight.base()), 3);
+}
+
+TEST(ANN_IR_WEIGHT, fill_vector_float)
+{
+ std::vector<float> values{1.0f, 2.0f};
+
+ ann::Weight weight;
+
+ weight.fill(values.begin(), values.end());
+
+ ASSERT_NE(weight.base(), nullptr);
+
+ auto arr = reinterpret_cast<const float *>(weight.base());
+
+ ASSERT_FLOAT_EQ(arr[0], 1.0f);
+ ASSERT_FLOAT_EQ(arr[1], 2.0f);
+}
diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.cpp b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp
new file mode 100644
index 000000000..d8809ac08
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/WeightInventory.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "WeightInventory.h"
+
+#include <stdex/Memory.h>
+
+using stdex::make_unique;
+
+namespace ann
+{
+
+Weight *WeightInventory::create(void)
+{
+ auto hnd = make_unique<Weight>();
+ auto ptr = hnd.get();
+ _weights.push_back(std::move(hnd));
+ return ptr;
+}
+
+} // namespace ann
diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.h b/compiler/enco/core/src/ANN/IR/WeightInventory.h
new file mode 100644
index 000000000..fd166837f
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/WeightInventory.h
@@ -0,0 +1,38 @@
+/*
+ * 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 __WEIGHT_INVENTORY_H__
+#define __WEIGHT_INVENTORY_H__
+
+#include "ANN/IR/Weight.h"
+
+#include <memory>
+
+namespace ann
+{
+
+class WeightInventory
+{
+public:
+ Weight *create(void);
+
+private:
+ std::vector<std::unique_ptr<Weight>> _weights;
+};
+
+} // namespace ann
+
+#endif // __WEIGHT_INVENTORY_H__
diff --git a/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp b/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp
new file mode 100644
index 000000000..143bdfddf
--- /dev/null
+++ b/compiler/enco/core/src/ANN/IR/WeightInventory.test.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "WeightInventory.h"
+
+#include <gtest/gtest.h>
+
+TEST(ANN_IR_WEIGHT_INVENTORY, create)
+{
+ ann::WeightInventory inven;
+
+ auto weight = inven.create();
+
+ ASSERT_EQ(weight->base(), nullptr);
+ ASSERT_EQ(weight->size(), 0);
+}
diff --git a/compiler/enco/core/src/AsmCode.cpp b/compiler/enco/core/src/AsmCode.cpp
new file mode 100644
index 000000000..70d6f30b3
--- /dev/null
+++ b/compiler/enco/core/src/AsmCode.cpp
@@ -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 "AsmCode.h"
+
+namespace enco
+{
+
+void AsmCode::dump(std::ostream &os) const
+{
+ os << ".section .rodata" << std::endl;
+ os << ".global " << _varname << std::endl;
+ // Please refer to https://www.sourceware.org/binutils/docs/as/Type.html#Type for details
+ os << ".type " << _varname << ", STT_OBJECT" << std::endl;
+ os << ".align " << 4 << std::endl;
+ os << _varname << ":" << std::endl;
+ os << ".incbin " << '"' << _filename << '"' << std::endl;
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/AsmCode.h b/compiler/enco/core/src/AsmCode.h
new file mode 100644
index 000000000..c43892888
--- /dev/null
+++ b/compiler/enco/core/src/AsmCode.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __ENCO_ASM_CODE_H__
+#define __ENCO_ASM_CODE_H__
+
+#include <ostream>
+#include <string>
+
+namespace enco
+{
+
+class AsmCode
+{
+public:
+ AsmCode(const std::string &filename, const std::string &varname)
+ : _filename{filename}, _varname{varname}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(std::ostream &) const;
+
+private:
+ std::string _filename;
+ std::string _varname;
+};
+
+} // namespace enco
+
+static inline std::ostream &operator<<(std::ostream &os, const enco::AsmCode &code)
+{
+ code.dump(os);
+ return os;
+}
+
+#endif // __ENCO_ASM_CODE_H__
diff --git a/compiler/enco/core/src/Backend.cpp b/compiler/enco/core/src/Backend.cpp
new file mode 100644
index 000000000..d4bec7447
--- /dev/null
+++ b/compiler/enco/core/src/Backend.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 "enco/Backend.h"
+
+#include "IRValidator.h"
+
+#include "Session.h"
+#include "Pipeline.h"
+
+#include "Code.h"
+#include "AsmCode.h"
+#include "CppCode.h"
+
+#include "Transforms/Duplicate.h"
+#include "Transforms/FeatureUnification.h"
+#include "Transforms/AvgPoolLowering.h"
+#include "Transforms/IntrinsicSelection.h"
+#include "Transforms/DataLayoutConversion.h"
+#include "Transforms/IndirectCopyElimination.h"
+#include "Transforms/IdenticalObjectReduction.h"
+#include "Transforms/DuplicatedObjectReduction.h"
+#include "Transforms/DeadObjectElimination.h"
+#include "Transforms/ConstantFolding.h"
+#include "Transforms/CopyLowering.h"
+#include "Transforms/ConcatLowering.h"
+#include "Transforms/FreeInstrElimination.h"
+#include "Transforms/FreeOpElimination.h"
+#include "Transforms/DeadBagElimination.h"
+#include "Transforms/Optimizations.h"
+#include "Transforms/Split.h"
+#include "Transforms/GlobalDataGeneration.h"
+
+#include <stdex/Memory.h>
+
+#include <stdexcept>
+#include <iostream>
+#include <fstream>
+
+using stdex::make_unique;
+using namespace enco;
+
+namespace
+{
+
+// has_inout_bag(m) returns true if there is a pair of coco::Input and coco::Output that share
+// the same bag as their backing storage
+inline bool has_inout_bag(const coco::Module *m)
+{
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ if (bag->isInput() && bag->isOutput())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+class BackendImpl final : public enco::Backend
+{
+public:
+ BackendImpl(const std::string &prefix) : _prefix{prefix}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void compile(coco::Module *m, coco::Data *d) override;
+
+private:
+ std::string _prefix;
+};
+
+void BackendImpl::compile(coco::Module *m, coco::Data *d)
+{
+ auto sess = make_session(m, d);
+
+ // validate if IR from frontend is correct
+ assert(validate(code(sess)));
+
+ enco::Pipeline pipeline;
+
+ // Configure pipeline
+
+ // As explained below, the current implementation does not work if there is a pair of input/output
+ // that share the same bag as their underlying bag.
+ //
+ // BagDuplicationPass creates a copy of such bags in order to eliminate such a pair.
+ pipeline.append(make_unique<BagDuplicationPass>());
+ pipeline.append(make_unique<FeatureUnificationPass>());
+ pipeline.append(make_unique<AvgPoolLoweringPass>());
+ pipeline.append(make_unique<IntrinsicSelectionPass>());
+ // Insert data ordering if necessary
+ pipeline.append(make_unique<DataLayoutConversionPass>());
+ pipeline.append(make_unique<IndirectCopyEliminationPass>());
+ pipeline.append(make_unique<IdenticalObjectReductionPass>());
+ pipeline.append(make_unique<DuplicatedObjectReductionPass>());
+ pipeline.append(make_unique<ConstantFoldingPass>());
+ // Eliminate dead object
+ //
+ // NOTE Dead Object Elimination (DOE) is performed before Copy lowering
+ // in order to reduce compilation overhead.
+ pipeline.append(make_unique<DeadObjectEliminationPass>());
+ // Lower Copy as Shuffle
+ pipeline.append(make_unique<CopyLoweringPass>());
+ // Lower ConcatF as Shuffle if it is not delegated to NNAPI yet
+ pipeline.append(make_unique<ConcatLoweringPass>());
+ pipeline.append(make_unique<BypassGenerationPass>());
+ pipeline.append(make_unique<FreeInstrEliminationPass>());
+ // NOTE Free Op Elimination should be applied after Free Instr Elimination
+ // - Free Instr Elimination may generate additional free Op(s)
+ pipeline.append(make_unique<FreeOpEliminationPass>());
+ pipeline.append(make_unique<DeadBagEliminationPass>());
+ // Split instructions into a set of phases (each block serves as a phase)
+ pipeline.append(make_unique<PhaseConstructionPass>());
+
+ // Apply transforms in the pipeline
+ for (uint32_t n = 0; n < pipeline.size(); ++n)
+ {
+ const auto &pass = pipeline.at(n);
+
+ pass.run(sess);
+ }
+
+ // The current implementation will assign memory region for each bag as follows:
+ // Bind input bag to the region provided by Network_input_bind
+ // Bind output bag to the region provided by Network_output_bind
+ // Bind intermediate bag to the region allocated during execution
+ //
+ // Note that this scheme does not work if there is a pair of input/output
+ // that share the same bag as their underlying bag
+ assert(!has_inout_bag(code(sess)->module()));
+
+ const std::string data_var = "data";
+ const std::string data_filename = _prefix + ".bin";
+
+ // Generate 'bin' file
+ {
+ std::ofstream ofs{data_filename, std::ios::binary};
+ generate_global_data(ofs, code(sess));
+ }
+
+ // Generate 'embed.S' file
+ {
+ std::ofstream ofs{_prefix + ".embed.S"};
+ ofs << AsmCode{data_filename, data_var};
+ }
+
+ // TODO Run various transforms over enco::Code
+
+ std::ofstream ofs{_prefix + ".cpp"};
+ ofs << CppCode{data_var, code(sess)} << std::endl;
+}
+
+} // namespace enco
+
+#include <iostream>
+
+std::unique_ptr<enco::Backend> make_backend(const cmdline::View &cmdline)
+{
+ return make_unique<::BackendImpl>(cmdline.at(0));
+}
diff --git a/compiler/enco/core/src/Code.h b/compiler/enco/core/src/Code.h
new file mode 100644
index 000000000..91756d5f8
--- /dev/null
+++ b/compiler/enco/core/src/Code.h
@@ -0,0 +1,47 @@
+/*
+ * 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 __ENCO_CODE_H__
+#define __ENCO_CODE_H__
+
+#include "ANN/Context.h"
+
+#include <coco/IR/Module.h>
+#include <coco/IR/Data.h>
+
+namespace enco
+{
+
+struct Code
+{
+public:
+ Code(coco::Module *module, coco::Data *data) : _module{module}, _data{data}
+ {
+ // DO NOTHING
+ }
+
+public:
+ coco::Module *module(void) const { return _module; }
+ coco::Data *data(void) const { return _data; }
+
+private:
+ coco::Module *const _module;
+ coco::Data *const _data;
+};
+
+} // namespace enco
+
+#endif // __ENCO_CODE_H__
diff --git a/compiler/enco/core/src/Code.test.cpp b/compiler/enco/core/src/Code.test.cpp
new file mode 100644
index 000000000..8e96e4751
--- /dev/null
+++ b/compiler/enco/core/src/Code.test.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "Code.h"
+
+#include <gtest/gtest.h>
+
+TEST(CODE, constructor)
+{
+ auto m = coco::Module::create();
+ auto d = coco::Data::create();
+
+ enco::Code code{m.get(), d.get()};
+
+ ASSERT_EQ(code.module(), m.get());
+ ASSERT_EQ(code.data(), d.get());
+}
diff --git a/compiler/enco/core/src/CodeIndex.h b/compiler/enco/core/src/CodeIndex.h
new file mode 100644
index 000000000..7f2da6463
--- /dev/null
+++ b/compiler/enco/core/src/CodeIndex.h
@@ -0,0 +1,76 @@
+/*
+ * 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 __CODE_INDEX_H__
+#define __CODE_INDEX_H__
+
+#include <coco/IR/Block.h>
+#include <coco/IR/Instr.h>
+
+/**
+ * @brief A CodeIndex denotes the index of instruction inside the whole module
+ */
+class CodeIndex
+{
+public:
+ CodeIndex() = default;
+
+public:
+ CodeIndex(const coco::BlockIndex &blk_ind, const coco::InstrIndex &ins_ind)
+ : _blk_ind{blk_ind}, _ins_ind{ins_ind}
+ {
+ }
+
+public:
+ const coco::BlockIndex &block(void) const { return _blk_ind; }
+ const coco::InstrIndex &instr(void) const { return _ins_ind; }
+
+private:
+ coco::BlockIndex _blk_ind;
+ coco::InstrIndex _ins_ind;
+};
+
+static inline coco::BlockIndex block_index(const coco::Block *blk)
+{
+ if (blk == nullptr)
+ {
+ return coco::BlockIndex{};
+ }
+
+ return blk->index();
+}
+
+static inline CodeIndex code_index(const coco::Instr *ins)
+{
+ return CodeIndex{block_index(ins->parent()), ins->index()};
+}
+
+static inline bool operator<(const CodeIndex &lhs, const CodeIndex &rhs)
+{
+ if (lhs.block() < rhs.block())
+ {
+ return true;
+ }
+
+ if (lhs.block().value() > rhs.block().value())
+ {
+ return false;
+ }
+
+ return lhs.instr() < rhs.instr();
+}
+
+#endif // __CODE_INDEX_H__
diff --git a/compiler/enco/core/src/CppCode.cpp b/compiler/enco/core/src/CppCode.cpp
new file mode 100644
index 000000000..aa5ef3156
--- /dev/null
+++ b/compiler/enco/core/src/CppCode.cpp
@@ -0,0 +1,553 @@
+/*
+ * 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 "CppCode.h"
+
+#include "Transforms/GlobalDataGeneration.h"
+#include "Transforms/Split.h"
+
+#include "CppGen/MemoryContext.h"
+
+#include "CppGen/Host.h"
+#include "CppGen/Subnet.h"
+
+#include "Dims.h"
+
+#include <pp/LinearDocument.h>
+#include <pp/MultiLineTextUtils.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <stdexcept>
+
+namespace
+{
+
+struct SubnetInfo
+{
+ std::string struct_name;
+ /// @brief The field name (in this subnet struct) of ANeuralNetworksCompilation value
+ std::string compilation_field;
+
+ /// @brief The field name (in Network struct) for this subnet
+ std::string field_name;
+};
+
+struct NetworkStruct
+{
+ pp::LinearDocument def;
+};
+
+struct InvokeFunction
+{
+ pp::LinearDocument head;
+ pp::LinearDocument body;
+ pp::LinearDocument tail{pp::LinearDocument::Direction::Reverse};
+
+public:
+ /** @brief Create a (fresh) local variable */
+ std::string local(void) { return pp::fmt("v_", ++_var_count); }
+
+private:
+ uint32_t _var_count = 0;
+};
+
+/**
+ * @brief Enumerate a set of Bag accessed by a given instruction
+ *
+ * Supported instruction:
+ * "Shuffle"
+ */
+class AccessedBagAccumulator : public coco::Instr::Visitor<void>
+{
+public:
+ AccessedBagAccumulator(std::set<coco::Bag *> *out) : _out{out}
+ {
+ // Validate "out"
+ assert(_out != nullptr);
+ }
+
+public:
+ void visit(const coco::Shuffle *shuffle) override
+ {
+ assert(shuffle->from() != nullptr);
+ assert(shuffle->into() != nullptr);
+
+ _out->insert(shuffle->from());
+ _out->insert(shuffle->into());
+ }
+
+private:
+ std::set<coco::Bag *> *_out;
+};
+
+/**
+ * @brief Return a set of bags that SHOULD have a host allocation
+ */
+std::set<coco::Bag *> hosted(const enco::Code *code)
+{
+ std::set<coco::Bag *> res;
+
+ auto m = code->module();
+ auto ann_ctx = enco::SubnetManager::context(m);
+
+ for (auto blk = m->block()->head(); blk; blk = blk->next())
+ {
+ if (auto ann_binder = ann_ctx->find(blk))
+ {
+ // Case: The current block is ANN-compatible
+
+ // Each ANN input SHOULD have a corresponding host allocation
+ for (uint32_t n = 0; n < ann_binder->module()->input()->size(); ++n)
+ {
+ res.insert(ann_binder->input(n));
+ }
+
+ // Each ANN output SHOULD have a corresponding host allocation
+ for (uint32_t n = 0; n < ann_binder->module()->output()->size(); ++n)
+ {
+ res.insert(ann_binder->output(n));
+ }
+ }
+ else
+ {
+ // Every bag that ANN-incompatible block accesses SHOULD have a corresponding host allocation
+ AccessedBagAccumulator acc{&res};
+
+ for (auto ins = blk->instr()->head(); ins; ins = ins->next())
+ {
+ ins->accept(acc);
+ }
+ }
+ }
+
+ return res;
+}
+} // namespace
+
+namespace enco
+{
+
+void CppCode::dump(std::ostream &os) const
+{
+ auto m = _code->module();
+ auto d = _code->data();
+ auto ann_ctx = enco::SubnetManager::context(m);
+
+ NetworkStruct network;
+ InvokeFunction invoke;
+ pp::LinearDocument internal;
+
+ auto data_exp = [this](const GlobalOffset &off) { return pp::fmt(_varname, " + ", off); };
+
+ // Record the subnet information
+ std::map<const ANNBinder *, SubnetInfo> subnet_ctx;
+
+ /**
+ * Create a struct for each android NN network of the following form:
+ *
+ * struct [Name]
+ * {
+ * ...
+ *
+ * [Name]() // constructor
+ * {
+ * ...
+ * }
+ *
+ * ~[Name]() // destructor
+ * {
+ * ...
+ * }
+ * };
+ *
+ */
+ for (uint32_t n = 0; n < ann_ctx->count(); ++n)
+ {
+ SubnetStructBuilder builder;
+
+ auto subnet_binder = ann_ctx->nth(n);
+ auto subnet_struct_name = pp::fmt("Subnet_", subnet_ctx.size());
+ auto subnet_field_name = pp::fmt("_subnet_", subnet_ctx.size());
+
+ // Create global data variable
+ auto emit_weight = [&](const ann::OperandID &, const ann::Operand *info) {
+ if (info->weight())
+ {
+ auto size = info->weight()->size();
+ auto off = enco::GlobalData::data_offset(info);
+ auto base_exp = pp::fmt("reinterpret_cast<const void *>(", data_exp(off), ")");
+ auto size_exp = pp::fmt(size);
+
+ builder.expr(info, base_exp, size_exp);
+ }
+ };
+ subnet_binder->module()->operand()->each(emit_weight);
+
+ auto subnet_struct_content = builder.build(subnet_binder);
+
+ // Emit C++ declaration
+ internal.append("struct ", subnet_struct_name);
+ internal.append("{");
+ internal.indent();
+
+ internal.append(subnet_struct_content->def());
+
+ internal.append(subnet_struct_name, "()");
+ internal.append("{");
+ internal.indent();
+ internal.append(subnet_struct_content->ctor());
+ internal.unindent();
+ internal.append("}");
+
+ internal.append("~", subnet_struct_name, "()");
+ internal.append("{");
+ internal.indent();
+ internal.append(subnet_struct_content->dtor());
+ internal.unindent();
+ internal.append("}");
+
+ internal.unindent();
+ internal.append("};");
+
+ // Declare subnet field
+ network.def.append(subnet_struct_name, " ", subnet_field_name, ";");
+
+ // Update subnet context
+ SubnetInfo subnet_info;
+
+ subnet_info.struct_name = subnet_struct_name;
+ subnet_info.compilation_field = subnet_struct_content->compilation();
+ subnet_info.field_name = subnet_field_name;
+
+ assert(subnet_ctx.find(subnet_binder) == subnet_ctx.end());
+ subnet_ctx[subnet_binder] = subnet_info;
+ }
+
+ MemoryContext mem;
+
+ // Set dedicated memory region for network inputs
+ for (uint32_t n = 0; n < m->input()->size(); ++n)
+ {
+ mem.base(m->input()->at(n)->bag(), pp::fmt("net->inputs[", n, "].ptr"));
+ mem.size(m->input()->at(n)->bag(), pp::fmt("net->inputs[", n, "].len"));
+ }
+
+ // Set dedicated memory region for network outputs
+ for (uint32_t n = 0; n < m->output()->size(); ++n)
+ {
+ mem.base(m->output()->at(n)->bag(), pp::fmt("net->outputs[", n, "].ptr"));
+ mem.size(m->output()->at(n)->bag(), pp::fmt("net->outputs[", n, "].len"));
+ }
+
+ // Set dedicated memory region for constant weight values
+ // TODO Support non-constant bags with initial values
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ if (!d->allocated(bag))
+ {
+ // Skip if no weight exists
+ continue;
+ }
+
+ // TODO Support non-float(fp32) weight
+ auto offset = enco::GlobalData::data_offset(bag);
+
+ auto base_expr = data_exp(offset);
+ auto size_expr = pp::fmt(bag->size() * sizeof(float));
+
+ mem.base(bag, base_expr);
+ mem.size(bag, size_expr);
+ }
+
+ // Set dedicated memory reigion for intermediate buffer(s)
+ for (const auto &bag : hosted(_code))
+ {
+ // Skip if a bag is already allocated
+ if (mem.member(bag))
+ {
+ continue;
+ }
+
+ auto name = invoke.local();
+
+ invoke.head.append("auto ", name, " = new uint8_t[", bag->size() * sizeof(float), "];");
+ invoke.tail.append("delete[] ", name, ";");
+
+ mem.base(bag, name);
+ mem.size(bag, pp::fmt(bag->size() * sizeof(float)));
+ }
+
+ // Create Code Block Builder
+ SubnetBlockCompiler subnet_compiler{mem};
+
+ for (auto it = subnet_ctx.begin(); it != subnet_ctx.end(); ++it)
+ {
+ // Specify how to access ANeuralNetworksCompilation
+ const auto &info = it->second;
+ subnet_compiler.bind(it->first, pp::fmt("net->", info.field_name, ".", info.compilation_field));
+ }
+
+ HostBlockCompiler host_compiler{mem};
+
+ for (auto blk = m->block()->head(); blk; blk = blk->next())
+ {
+ invoke.body.append("{");
+ invoke.body.indent();
+
+ if (auto binder = ann_ctx->find(blk))
+ {
+ // Generate code that invokes Android NN sub-network
+ auto lines = subnet_compiler.compile(binder);
+ invoke.body.append(*lines);
+ }
+ else
+ {
+ // Generate code on-the-fly for Android NN-incompatible blocks
+ auto lines = host_compiler.compile(blk);
+ invoke.body.append(*lines);
+ }
+
+ invoke.body.unindent();
+ invoke.body.append("}");
+ }
+
+ //
+ // Generate full C++ source code with code snippet
+ //
+ const std::string name{"Network"};
+
+ pp::LinearDocument includes;
+ {
+ // Include Android NN API header
+ includes.append("#include <NeuralNetworks.h>");
+ includes.append();
+
+ includes.append("#include <cstdint>");
+ includes.append("#include <cassert>");
+ includes.append("#include <array>");
+ }
+
+ pp::LinearDocument net_def;
+ {
+ net_def.append("struct ", name, " {");
+ net_def.indent();
+ net_def.append("struct Shape { uint32_t rank; const uint32_t *dims; };");
+ net_def.append("struct Input {");
+ net_def.indent();
+ net_def.append("const char *name;");
+ net_def.append("const uint8_t *ptr;");
+ net_def.append("unsigned len;");
+ net_def.append("Shape shape;");
+ net_def.unindent();
+ net_def.append("};");
+ net_def.append("struct Output {");
+ net_def.indent();
+ net_def.append("const char *name;");
+ net_def.append("uint8_t *ptr;");
+ net_def.append("unsigned len;");
+ net_def.append("Shape shape;");
+ net_def.unindent();
+ net_def.append("};");
+ net_def.append();
+ net_def.append(name, "();");
+ net_def.append("~", name, "();");
+
+ net_def.append();
+ net_def.append(network.def);
+ net_def.append();
+
+ net_def.append("std::array<Input, ", m->input()->size(), "> inputs;");
+ net_def.append("std::array<Output, ", m->output()->size(), "> outputs;");
+
+ net_def.unindent();
+ net_def.append("};");
+ }
+
+ pp::LinearDocument net_ctor;
+ {
+ net_ctor.append("Network::Network() {");
+ net_ctor.indent();
+
+ // Initialize input metadata
+ for (uint32_t n = 0; n < m->input()->size(); ++n)
+ {
+ auto input = m->input()->at(n);
+ auto dims = as_dims(input->shape());
+
+ auto name_off = enco::GlobalData::name_offset(input);
+ auto name_exp = pp::fmt("reinterpret_cast<const char *>(", data_exp(name_off), ")");
+ auto dims_off = enco::GlobalData::dims_offset(input);
+ auto dims_exp = pp::fmt("reinterpret_cast<const unsigned *>(", data_exp(dims_off), ")");
+
+ net_ctor.append("inputs.at(", n, ").name = ", name_exp, ";");
+ net_ctor.append("inputs.at(", n, ").shape.rank = ", dims.size(), ";");
+ net_ctor.append("inputs.at(", n, ").shape.dims = ", dims_exp, ";");
+ }
+
+ // Initialize output metadata
+ for (uint32_t n = 0; n < m->output()->size(); ++n)
+ {
+ auto output = m->output()->at(n);
+ auto dims = as_dims(output->shape());
+
+ auto name_off = enco::GlobalData::name_offset(output);
+ auto name_exp = pp::fmt("reinterpret_cast<const char *>(", data_exp(name_off), ")");
+ auto dims_off = enco::GlobalData::dims_offset(output);
+ auto dims_exp = pp::fmt("reinterpret_cast<const unsigned *>(", data_exp(dims_off), ")");
+
+ net_ctor.append("outputs.at(", n, ").name = ", name_exp, ";");
+ net_ctor.append("outputs.at(", n, ").shape.rank = ", dims.size(), ";");
+ net_ctor.append("outputs.at(", n, ").shape.dims = ", dims_exp, ";");
+ }
+
+ // TODO Implement this
+ net_ctor.unindent();
+ net_ctor.append("}");
+ }
+
+ pp::LinearDocument net_dtor;
+ {
+ net_dtor.append("Network::~Network() {");
+ net_dtor.indent();
+ // TODO Implement this
+ net_dtor.unindent();
+ net_dtor.append("}");
+ }
+
+ pp::LinearDocument source;
+
+ source.append(includes);
+ source.append();
+ source.append("extern uint8_t ", _varname, "[];");
+ source.append();
+
+ source.append("namespace");
+ source.append("{");
+ source.append(internal);
+ source.append("} // namespace");
+ source.append();
+ source.append(net_def);
+ source.append();
+ source.append(net_ctor);
+ source.append();
+ source.append(net_dtor);
+
+ source.append();
+ source.append(name, " *", name, "_construct() { return new ", name, "{}; }");
+ source.append("void ", name, "_destruct(", name, " *net) { delete net; }");
+
+ source.append();
+
+ // Emit Network_input_count function
+ source.append("unsigned ", name, "_input_count(const ", name, " *net) {");
+ source.indent();
+ source.append("return net->inputs.size();");
+ source.unindent();
+ source.append("}");
+
+ source.append();
+
+ // Emit Network_input_name function
+ source.append("const char *", name, "_input_name(const ", name, " *net, unsigned n) {");
+ source.indent();
+ source.append("return net->inputs.at(n).name;");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_input_rank function
+ source.append("unsigned ", name, "_input_rank(const ", name, " *net, unsigned n) {");
+ source.indent();
+ source.append("return net->inputs.at(n).shape.rank;");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_input_dim function
+ source.append("unsigned ", name, "_input_dim(const ", name, " *net, unsigned n, unsigned axe)");
+ source.append("{");
+ source.indent();
+ source.append("return net->inputs.at(n).shape.dims[axe];");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_input_bind function
+ source.append("void ", name, "_input_bind(", name,
+ " *net, unsigned n, const void *ptr, unsigned len) {");
+ source.indent();
+ source.append("net->inputs.at(n).ptr = reinterpret_cast<const uint8_t *>(ptr);");
+ source.append("net->inputs.at(n).len = len;");
+ source.unindent();
+ source.append("}");
+
+ source.append();
+
+ // Emit Network_output_count function
+ source.append("unsigned ", name, "_output_count(const ", name, " *net) {");
+ source.indent();
+ source.append("return net->outputs.size();");
+ source.unindent();
+ source.append("}");
+
+ source.append();
+
+ // Emit Network_output_name function
+ source.append("const char *", name, "_output_name(const ", name, " *net, unsigned n) {");
+ source.indent();
+ source.append("return net->outputs.at(n).name;");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_output_rank function
+ source.append("unsigned ", name, "_output_rank(const ", name, " *net, unsigned n) {");
+ source.indent();
+ source.append("return net->outputs.at(n).shape.rank;");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_output_dim function
+ source.append("unsigned ", name, "_output_dim(const ", name, " *net, unsigned n, unsigned axe)");
+ source.append("{");
+ source.indent();
+ source.append("return net->outputs.at(n).shape.dims[axe];");
+ source.unindent();
+ source.append("}");
+
+ // Emit Network_output_bind function
+ source.append("void ", name, "_output_bind(", name,
+ " *net, unsigned n, void *ptr, unsigned len) {");
+ source.indent();
+ source.append("net->outputs.at(n).ptr = reinterpret_cast<uint8_t *>(ptr);");
+ source.append("net->outputs.at(n).len = len;");
+ source.unindent();
+ source.append("}");
+
+ source.append();
+
+ source.append("void ", name, "_invoke(", name, " *net) {");
+ source.indent();
+ source.append(invoke.head);
+ source.append(invoke.body);
+ source.append(invoke.tail);
+ source.unindent();
+ source.append("}");
+
+ os << source;
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/CppCode.h b/compiler/enco/core/src/CppCode.h
new file mode 100644
index 000000000..c52ea1d5d
--- /dev/null
+++ b/compiler/enco/core/src/CppCode.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __ENCO_CPP_CODE_H__
+#define __ENCO_CPP_CODE_H__
+
+#include "Code.h"
+
+#include <ostream>
+
+namespace enco
+{
+
+class CppCode
+{
+public:
+ CppCode(const std::string &varname, const Code *code) : _varname{varname}, _code{code}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(std::ostream &) const;
+
+private:
+ const std::string _varname;
+ const Code *_code;
+};
+
+} // namespace enco
+
+static inline std::ostream &operator<<(std::ostream &os, const enco::CppCode &code)
+{
+ code.dump(os);
+ return os;
+}
+
+#endif // __ENCO_CPP_CODE_H__
diff --git a/compiler/enco/core/src/CppGen/Host.cpp b/compiler/enco/core/src/CppGen/Host.cpp
new file mode 100644
index 000000000..37e0583d7
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/Host.cpp
@@ -0,0 +1,306 @@
+/*
+ * 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 "Host.h"
+
+#include <pp/EnclosedDocument.h>
+
+#include <stdex/Memory.h>
+
+#include <map>
+#include <string>
+
+namespace
+{
+
+/**
+ * @brief Data transfer between flat arrays
+ *
+ * Transfer(from, into) denotes the following C code:
+ * dst[into] = src[from];
+ */
+class Transfer
+{
+public:
+ Transfer(uint32_t from, uint32_t into) : _from{from}, _into{into}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t from(void) const { return _from; }
+ uint32_t into(void) const { return _into; }
+
+private:
+ uint32_t _from;
+ uint32_t _into;
+};
+
+using TransferSequence = std::vector<Transfer>;
+
+/**
+ * @brief Convert Shuffle instruction as a sequence of data transfer
+ */
+TransferSequence as_transfer_sequence(const coco::Shuffle *shuffle)
+{
+ TransferSequence seq;
+
+ for (const auto &dst : shuffle->range())
+ {
+ const auto src = shuffle->at(dst);
+ seq.emplace_back(src.value(), dst.value());
+ }
+
+ return seq;
+}
+
+/**
+ * Given a sequence of N data transfers,
+ * find_loop tries to compute count, src_step, dst_step that satisfies
+ * the following properties:
+ *
+ * First, N should be a multiple of count.
+ * Below we refer to that multiplier as 'window' (= N / count)
+ *
+ * Second,
+ * for all n in [0, count),
+ * for all k in [0, window),
+ * from[n * window + k] == from[k] + src_step, and
+ * into[n * window + k] == into[k] + dst_step
+ */
+bool find_loop(TransferSequence::const_iterator beg, TransferSequence::const_iterator end,
+ uint32_t *p_count, uint32_t *p_src_step, uint32_t *p_dst_step)
+{
+ assert(p_count != nullptr);
+ assert(p_src_step != nullptr);
+ assert(p_dst_step != nullptr);
+
+ const uint32_t size = end - beg;
+
+ for (uint32_t window = 1; window <= size; ++window)
+ {
+ if (size % window != 0)
+ {
+ continue;
+ }
+
+ auto src_step_at = [&beg, window](uint32_t n) {
+ return (beg + n)->from() - (beg + n - window)->from();
+ };
+
+ auto dst_step_at = [&beg, window](uint32_t n) {
+ return (beg + n)->into() - (beg + n - window)->into();
+ };
+
+ const uint32_t count = size / window;
+ const uint32_t src_step = src_step_at(window);
+ const uint32_t dst_step = dst_step_at(window);
+
+ bool consistent = true;
+
+ for (uint32_t n = window + 1; n < size; ++n)
+ {
+ if ((src_step_at(n) != src_step) || (dst_step_at(n) != dst_step))
+ {
+ consistent = false;
+ break;
+ }
+ }
+
+ if (consistent)
+ {
+ *p_count = count;
+ *p_src_step = src_step;
+ *p_dst_step = dst_step;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @brief Single transfer loop (a triple of count, source step, detination step)
+ */
+class TransferLoop
+{
+public:
+ class Step
+ {
+ public:
+ Step(uint32_t src, uint32_t dst) : _src{src}, _dst{dst}
+ {
+ // DO NOTHING
+ }
+
+ public:
+ uint32_t src(void) const { return _src; }
+ uint32_t dst(void) const { return _dst; }
+
+ private:
+ uint32_t _src;
+ uint32_t _dst;
+ };
+
+public:
+ TransferLoop(uint32_t count, uint32_t src_step, uint32_t dst_step)
+ : _count{count}, _step{src_step, dst_step}
+ {
+ // DO NOTHING
+ }
+
+public:
+ uint32_t count(void) const { return _count; }
+ const Step &step(void) const { return _step; }
+
+private:
+ uint32_t _count;
+ Step _step;
+};
+
+/**
+ * @brief Nested transfer loops
+ */
+using TransferNest = std::vector<TransferLoop>;
+
+/**
+ * @brief Construct nested transfer loop-nest that correponds to a given Shuffle instruction
+ */
+TransferNest as_nest(const TransferSequence &seq)
+{
+ TransferNest nest;
+
+ auto beg = seq.begin();
+ auto end = seq.end();
+
+ uint32_t window = end - beg;
+ uint32_t count = 0;
+ uint32_t src_step = 0;
+ uint32_t dst_step = 0;
+
+ while ((window > 1) && find_loop(beg, end, &count, &src_step, &dst_step))
+ {
+ assert(window % count == 0);
+
+ window /= count;
+ end = beg + window;
+
+ nest.emplace_back(count, src_step, dst_step);
+ }
+
+ return nest;
+};
+
+uint32_t loop_count(const TransferNest &nest)
+{
+ uint32_t count = 1;
+
+ for (const auto &loop : nest)
+ {
+ count *= loop.count();
+ }
+
+ return count;
+};
+
+class InstrPrinter : public coco::Instr::Visitor<pp::LinearDocument>
+{
+public:
+ InstrPrinter(const enco::MemoryContext &mem) : _mem(mem)
+ {
+ // DO NOTHING
+ }
+
+private:
+ pp::LinearDocument visit(const coco::Shuffle *shuffle) override
+ {
+ auto from = shuffle->from();
+ auto into = shuffle->into();
+
+ //
+ // Analyze 'Shuffle' pattern, and convert it as nested loops
+ //
+ auto tseq = as_transfer_sequence(shuffle);
+ auto nest = as_nest(tseq);
+ assert(tseq.size() % loop_count(nest) == 0);
+ uint32_t window = tseq.size() / loop_count(nest);
+
+ //
+ // Generate loop body
+ //
+ pp::EnclosedDocument loop_body;
+
+ auto var_at = [](uint32_t lv) { return pp::fmt("_", lv); };
+
+ for (uint32_t lv = 0; lv < nest.size(); ++lv)
+ {
+ auto var = var_at(lv);
+
+ loop_body.front().append("for (uint32_t ", var, " = 0; ", var, " < ", nest.at(lv).count(),
+ "; ++", var, ") {");
+ loop_body.front().indent();
+
+ loop_body.back().append("}");
+ loop_body.back().indent();
+ }
+
+ std::string src_index = "0";
+ std::string dst_index = "0";
+
+ for (uint32_t lv = 0; lv < nest.size(); ++lv)
+ {
+ src_index += pp::fmt(" + ", nest.at(lv).step().src(), " * ", var_at(lv));
+ dst_index += pp::fmt(" + ", nest.at(lv).step().dst(), " * ", var_at(lv));
+ }
+
+ for (uint32_t n = 0; n < window; ++n)
+ {
+ const auto src_base = pp::fmt("reinterpret_cast<const float *>(", _mem.base(from), ")");
+ const auto dst_base = pp::fmt("reinterpret_cast<float *>(", _mem.base(into), ")");
+
+ loop_body.front().append(dst_base, "[", dst_index, " + ", tseq.at(n).into(), "] = ", src_base,
+ "[", src_index, " + ", tseq.at(n).from(), "];");
+ }
+
+ pp::LinearDocument res;
+ res.append(loop_body);
+ return res;
+ }
+
+private:
+ const enco::MemoryContext &_mem;
+};
+
+} // namespace
+
+namespace enco
+{
+
+std::unique_ptr<pp::MultiLineText> HostBlockCompiler::compile(const coco::Block *blk) const
+{
+ InstrPrinter prn{_mem};
+
+ auto res = stdex::make_unique<pp::LinearDocument>();
+
+ for (auto ins = blk->instr()->head(); ins; ins = ins->next())
+ {
+ res->append(ins->accept(prn));
+ }
+
+ return std::move(res);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/CppGen/Host.h b/compiler/enco/core/src/CppGen/Host.h
new file mode 100644
index 000000000..0adb7fe1f
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/Host.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __ENCO_CPP_GEN_HOST_H__
+#define __ENCO_CPP_GEN_HOST_H__
+
+#include "CppGen/MemoryContext.h"
+
+#include <coco/IR.h>
+#include <pp/MultiLineText.h>
+
+namespace enco
+{
+
+/***
+ * @brief Generate C++ code that does not depend on Anroid NN API
+ */
+class HostBlockCompiler
+{
+public:
+ HostBlockCompiler(const enco::MemoryContext &mem) : _mem(mem)
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::unique_ptr<pp::MultiLineText> compile(const coco::Block *blk) const;
+
+private:
+ const enco::MemoryContext &_mem;
+};
+
+} // namespace enco
+
+#endif // __ENCO_CPP_GEN_HOST_H__
diff --git a/compiler/enco/core/src/CppGen/MemoryContext.cpp b/compiler/enco/core/src/CppGen/MemoryContext.cpp
new file mode 100644
index 000000000..e522968a8
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/MemoryContext.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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 "MemoryContext.h"
+
+#include <cassert>
+
+namespace enco
+{
+
+bool MemoryContext::member(const coco::Bag *bag) const
+{
+ // NOTE _base and _size SHOULD BE consistent
+ if (_base.find(bag) != _base.end())
+ {
+ assert(_size.find(bag) != _size.end());
+ return true;
+ }
+
+ assert(_size.find(bag) == _size.end());
+ return false;
+}
+
+void MemoryContext::base(const coco::Bag *bag, const std::string &exp) { _base[bag] = exp; }
+void MemoryContext::size(const coco::Bag *bag, const std::string &exp) { _size[bag] = exp; }
+
+} // namespace enco
diff --git a/compiler/enco/core/src/CppGen/MemoryContext.h b/compiler/enco/core/src/CppGen/MemoryContext.h
new file mode 100644
index 000000000..99c20f3e8
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/MemoryContext.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __ENCO_CPP_GEN_MEMORY_CONTEXT_H__
+#define __ENCO_CPP_GEN_MEMORY_CONTEXT_H__
+
+#include <coco/IR/Bag.h>
+
+#include <string>
+#include <map>
+
+namespace enco
+{
+
+/**
+ * @brief Record C/C++ expression that denotes the base and size of memory region
+ * dedicated to each bag
+ */
+class MemoryContext
+{
+public:
+ /**
+ * @brief Check whether a base/size expression for a given bag
+ */
+ bool member(const coco::Bag *bag) const;
+
+public:
+ void base(const coco::Bag *bag, const std::string &exp);
+ void size(const coco::Bag *bag, const std::string &exp);
+
+public:
+ const std::string &base(const coco::Bag *bag) const { return _base.at(bag); }
+ const std::string &size(const coco::Bag *bag) const { return _size.at(bag); }
+
+private:
+ std::map<const coco::Bag *, std::string> _base;
+ std::map<const coco::Bag *, std::string> _size;
+};
+
+} // namespace enco
+
+#endif // __ENCO_CPP_GEN_MEMORY_CONTEXT_H__
diff --git a/compiler/enco/core/src/CppGen/Subnet.cpp b/compiler/enco/core/src/CppGen/Subnet.cpp
new file mode 100644
index 000000000..9a636c6ae
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/Subnet.cpp
@@ -0,0 +1,422 @@
+/*
+ * 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 "CppGen/Subnet.h"
+
+#include "Dims.h"
+#include "String.h"
+
+#include <pp/LinearDocument.h>
+
+#include <stdex/Memory.h>
+
+#include <sstream>
+
+using stdex::make_unique;
+using enco::concat;
+
+#define S(content) #content
+
+namespace ann
+{
+static std::ostream &operator<<(std::ostream &os, const ann::OperandID &id)
+{
+ os << id.value();
+ return os;
+}
+} // namespace ann
+
+namespace
+{
+
+class SubnetStructImpl final : public enco::SubnetStruct
+{
+public:
+ SubnetStructImpl() : _dtor{pp::LinearDocument::Direction::Reverse}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::string model(void) const override { return "_model"; }
+ std::string compilation(void) const override { return "_compilation"; }
+
+public:
+ const pp::MultiLineText &def(void) const override { return _def; }
+ pp::LinearDocument *def(void) { return &_def; }
+
+public:
+ const pp::MultiLineText &ctor(void) const override { return _ctor; }
+ pp::LinearDocument *ctor(void) { return &_ctor; }
+
+public:
+ const pp::MultiLineText &dtor(void) const override { return _dtor; }
+ pp::LinearDocument *dtor(void) { return &_dtor; }
+
+private:
+ pp::LinearDocument _def;
+ pp::LinearDocument _ctor;
+ pp::LinearDocument _dtor;
+};
+
+struct CodeFragment
+{
+ virtual ~CodeFragment() = default;
+
+ virtual void dump(pp::LinearDocument *) const = 0;
+};
+
+pp::LinearDocument *operator<<(pp::LinearDocument *doc, const CodeFragment &fragment)
+{
+ fragment.dump(doc);
+ return doc;
+}
+
+const char *scalar_operand_code(const ann::DType &dtype)
+{
+ switch (dtype)
+ {
+ case ann::DType::S32:
+ return "ANEURALNETWORKS_INT32";
+ default:
+ break;
+ };
+
+ throw std::invalid_argument("dtype");
+}
+
+const char *tensor_operand_code(const ann::DType &dtype)
+{
+ switch (dtype)
+ {
+ case ann::DType::S32:
+ return "ANEURALNETWORKS_TENSOR_INT32";
+ case ann::DType::F32:
+ return "ANEURALNETWORKS_TENSOR_FLOAT32";
+ default:
+ break;
+ };
+
+ throw std::invalid_argument("dtype");
+}
+
+class ScalarOperandDecl final : public CodeFragment
+{
+public:
+ ScalarOperandDecl(const std::string &model, const ann::DType &dtype)
+ : _model{model}, _dtype{dtype}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(pp::LinearDocument *doc) const override
+ {
+ doc->append("{");
+ doc->indent();
+ doc->append("ANeuralNetworksOperandType t;");
+ doc->append();
+ doc->append("t.type = ", scalar_operand_code(_dtype), ";");
+ doc->append("t.dimensionCount = 0;");
+ doc->append("t.dimensions = nullptr;");
+ doc->append("t.scale = 1.0f;");
+ doc->append("t.zeroPoint = 0;");
+ doc->append();
+ doc->append("ANeuralNetworksModel_addOperand(", _model, ", &t);");
+ doc->unindent();
+ doc->append("}");
+ }
+
+private:
+ std::string _model;
+ ann::DType _dtype;
+};
+
+class TensorOperandDecl final : public CodeFragment
+{
+public:
+ TensorOperandDecl(const std::string &model, const ann::DType &dtype,
+ const nncc::core::ADT::tensor::Shape &shape)
+ : _model{model}, _dtype{dtype}, _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(pp::LinearDocument *doc) const override
+ {
+ const auto rank = _shape.rank();
+ const auto dims = as_dims(_shape);
+
+ assert(rank == dims.size());
+
+ doc->append("{");
+ doc->indent();
+ doc->append("uint32_t d[", rank, "] = { ", concat(", ", dims.begin(), dims.end()), " };");
+ doc->append();
+ doc->append("ANeuralNetworksOperandType t;");
+ doc->append();
+ doc->append("t.type = ", tensor_operand_code(_dtype), ";");
+ doc->append("t.dimensionCount = ", rank, ";");
+ doc->append("t.dimensions = d;");
+ doc->append("t.scale = 1.0f;");
+ doc->append("t.zeroPoint = 0;");
+ doc->append();
+ doc->append("ANeuralNetworksModel_addOperand(", _model, ", &t);");
+ doc->unindent();
+ doc->append("}");
+ }
+
+private:
+ std::string _model;
+ ann::DType _dtype;
+ nncc::core::ADT::tensor::Shape _shape;
+};
+
+/**
+ * @brief Code fragment that calls ANeuralNetworksModel_setOperandValue
+ */
+class WeightDecl final : public CodeFragment
+{
+public:
+ WeightDecl(const std::string &model, const ann::OperandID &id, const std::string &base,
+ const std::string &size)
+ : _model{model}, _id{id}, _base{base}, _size{size}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(pp::LinearDocument *doc) const override
+ {
+ doc->append("ANeuralNetworksModel_setOperandValue(", _model, ", ", _id.value(), ", ", _base,
+ ", ", _size, ");");
+ }
+
+private:
+ std::string _model;
+ ann::OperandID _id;
+ std::string _base;
+ std::string _size;
+};
+
+/**
+ * @brief Code fragment that calls ANeuralNetworksModel_addOperation
+ */
+class OperationDecl final : public CodeFragment
+{
+public:
+ OperationDecl(const std::string &model, const ann::Operation *op) : _model{model}, _op{op}
+ {
+ // DO NOTHING
+ }
+
+private:
+ static std::string opcode(const ann::Operation::Code &code)
+ {
+ switch (code)
+ {
+#define ANN_OPERATION(TAG, ENUM) \
+ case ann::Operation::Code::TAG: \
+ return #ENUM;
+#include "ANN/IR/Operation.def"
+#undef ANN_OPERATION
+ default:
+ throw std::invalid_argument{"code"};
+ };
+ }
+
+public:
+ void dump(pp::LinearDocument *doc) const override
+ {
+ const auto in_count = _op->inputs().size();
+ auto in_beg = _op->inputs().begin();
+ auto in_end = _op->inputs().end();
+
+ const auto out_count = _op->outputs().size();
+ auto out_beg = _op->outputs().begin();
+ auto out_end = _op->outputs().end();
+
+ auto op = opcode(_op->code());
+
+ doc->append("{");
+ doc->indent();
+ doc->append("uint32_t inputs[", in_count, "] = { ", concat(", ", in_beg, in_end), " };");
+ doc->append("uint32_t outputs[", out_count, "] = { ", concat(", ", out_beg, out_end), " };");
+ doc->append();
+ doc->append("ANeuralNetworksModel_addOperation(", _model, ", ", op, ", ", in_count,
+ ", inputs, ", out_count, ", outputs);");
+ doc->unindent();
+ doc->append("}");
+ }
+
+private:
+ std::string _model;
+ const ann::Operation *_op;
+};
+
+/**
+ * @brief Code fragment that calls ANeuralNetworksModel_identifyInputsAndOutputs
+ */
+class ArgumentDecl final : public CodeFragment
+{
+public:
+ ArgumentDecl(const std::string &mname, const ANNBinder *binder) : _mname{mname}, _binder{binder}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void dump(pp::LinearDocument *doc) const override
+ {
+ doc->append("{");
+ doc->indent();
+
+ auto module = _binder->module();
+ const uint32_t input_count = module->input()->size();
+
+ doc->append("uint32_t inputs[", input_count, "];");
+ for (uint32_t n = 0; n < input_count; ++n)
+ {
+ doc->append("inputs[", n, "] = ", module->input()->at(n), ";");
+ }
+
+ const uint32_t output_count = module->output()->size();
+
+ doc->append("uint32_t outputs[", output_count, "];");
+ for (uint32_t n = 0; n < output_count; ++n)
+ {
+ doc->append("outputs[", n, "] = ", module->output()->at(n), ";");
+ }
+
+ doc->append("ANeuralNetworksModel_identifyInputsAndOutputs(", _mname, ", ", input_count,
+ ", inputs, ", output_count, ", outputs);");
+ doc->unindent();
+ doc->append("}");
+ }
+
+private:
+ std::string _mname;
+ const ANNBinder *_binder;
+};
+
+} // namespace
+
+namespace enco
+{
+
+std::unique_ptr<SubnetStruct> SubnetStructBuilder::build(const ANNBinder *binder) const
+{
+ auto res = make_unique<SubnetStructImpl>();
+
+ auto mname = res->model();
+ auto cname = res->compilation();
+
+ res->def()->append("ANeuralNetworksModel *", mname, ";");
+ res->def()->append("ANeuralNetworksCompilation *", cname, ";");
+
+ res->ctor()->append("ANeuralNetworksModel_create(&", mname, ");");
+ res->dtor()->append("ANeuralNetworksModel_free(", mname, ");");
+
+ binder->module()->operand()->each([&](const ann::OperandID &id, const ann::Operand *info) {
+ // TODO Remove dynamic cast
+ if (auto scalar = dynamic_cast<const ann::ScalarOperand *>(info))
+ {
+ res->ctor() << ScalarOperandDecl{mname, scalar->dtype()};
+ }
+ else if (auto tensor = dynamic_cast<const ann::TensorOperand *>(info))
+ {
+ res->ctor() << TensorOperandDecl{mname, tensor->dtype(), tensor->shape()};
+ }
+ else
+ {
+ throw std::runtime_error{"Unsupported"};
+ }
+
+ if (_weighted.find(info) != _weighted.end())
+ {
+ const auto &base_exp = _base_exprs.at(info);
+ const auto &size_exp = _size_exprs.at(info);
+
+ res->ctor() << WeightDecl{mname, id, base_exp, size_exp};
+ }
+ });
+
+ for (unsigned n = 0; n < binder->module()->operation()->count(); ++n)
+ {
+ auto op = binder->module()->operation()->at(n);
+ res->ctor() << OperationDecl{mname, op};
+ }
+
+ // Emit ANeuralNetworksModel_identifyInputsAndOutputs call
+ res->ctor() << ArgumentDecl{mname, binder};
+
+ // Emit ANeuralNetworksModel_finish call
+ res->ctor()->append("ANeuralNetworksModel_finish(", mname, ");");
+
+ // Create compilation
+ res->ctor()->append("ANeuralNetworksCompilation_create(", mname, ", &", cname, ");");
+ res->dtor()->append("ANeuralNetworksCompilation_free(", cname, ");");
+
+ // Finalize compilation
+ res->ctor()->append("ANeuralNetworksCompilation_finish(", cname, ");");
+
+ return std::move(res);
+}
+
+std::unique_ptr<pp::MultiLineText> SubnetBlockCompiler::compile(const ANNBinder *binder) const
+{
+ auto res = make_unique<pp::LinearDocument>();
+
+ const auto compilation = _compilation_ctx.at(binder);
+
+ res->append("ANeuralNetworksExecution *execution;");
+ res->append("ANeuralNetworksEvent *event;");
+ res->append();
+ res->append("ANeuralNetworksExecution_create(", compilation, ", &execution);");
+
+ // Emit ANeuralNetworksExecution_setInput call(s)
+ for (uint32_t n = 0; n < binder->module()->input()->size(); ++n)
+ {
+ auto bag = binder->input(n);
+ auto base = _mem.base(bag);
+ auto size = _mem.size(bag);
+
+ res->append("ANeuralNetworksExecution_setInput(execution, ", n, ", nullptr, ", base, ", ", size,
+ ");");
+ }
+
+ // Emit ANeuralNetworksExecution_setOutput call(s)
+ for (uint32_t n = 0; n < binder->module()->output()->size(); ++n)
+ {
+ auto bag = binder->output(n);
+ auto base = _mem.base(bag);
+ auto size = _mem.size(bag);
+
+ res->append("ANeuralNetworksExecution_setOutput(execution, ", n, ", nullptr, ", base, ", ",
+ size, ");");
+ }
+
+ res->append("ANeuralNetworksExecution_startCompute(execution, &event);");
+ res->append("ANeuralNetworksEvent_wait(event);");
+ res->append("ANeuralNetworksEvent_free(event);");
+
+ res->append("ANeuralNetworksExecution_free(execution);");
+
+ return std::move(res);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/CppGen/Subnet.h b/compiler/enco/core/src/CppGen/Subnet.h
new file mode 100644
index 000000000..4a5738876
--- /dev/null
+++ b/compiler/enco/core/src/CppGen/Subnet.h
@@ -0,0 +1,91 @@
+/*
+ * 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 __ENCO_CPP_GEN_SUBNET_H__
+#define __ENCO_CPP_GEN_SUBNET_H__
+
+#include "ANN/Binder.h"
+#include "CppGen/MemoryContext.h"
+
+#include <pp/MultiLineText.h>
+#include <map>
+#include <set>
+
+namespace enco
+{
+
+/**
+ * @brief A C++ struct that provides Android NN model & compilation
+ */
+struct SubnetStruct
+{
+ virtual ~SubnetStruct() = default;
+
+ /// @brief Return the field name of ANeuralNetworksModel value
+ virtual std::string model(void) const = 0;
+ /// @brief Return the field name of ANeuralNetworksCompilatoin value
+ virtual std::string compilation(void) const = 0;
+
+ virtual const pp::MultiLineText &def(void) const = 0;
+ virtual const pp::MultiLineText &ctor(void) const = 0;
+ virtual const pp::MultiLineText &dtor(void) const = 0;
+};
+
+class SubnetStructBuilder
+{
+public:
+ std::unique_ptr<SubnetStruct> build(const ANNBinder *binder) const;
+
+public:
+ void expr(const ann::Operand *oper, const std::string &base, const std::string &size)
+ {
+ _weighted.insert(oper);
+ _base_exprs[oper] = base;
+ _size_exprs[oper] = size;
+ }
+
+private:
+ std::set<const ann::Operand *> _weighted;
+ std::map<const ann::Operand *, std::string> _base_exprs;
+ std::map<const ann::Operand *, std::string> _size_exprs;
+};
+
+/**
+ * @brief Generate C++ code that invokes Android NN subnet
+ */
+class SubnetBlockCompiler
+{
+public:
+ SubnetBlockCompiler(const enco::MemoryContext &mem) : _mem(mem)
+ {
+ // DO NOTHING
+ }
+
+public:
+ /// @brief Specify how to access ANeuralNetworksCompilation value (C expression)
+ void bind(const ANNBinder *binder, const std::string &exp) { _compilation_ctx[binder] = exp; }
+
+public:
+ std::unique_ptr<pp::MultiLineText> compile(const ANNBinder *binder) const;
+
+private:
+ const enco::MemoryContext &_mem;
+ std::map<const ANNBinder *, std::string> _compilation_ctx;
+};
+
+} // namespace enco
+
+#endif // __ENCO_CPP_GEN_SUBNET_H__
diff --git a/compiler/enco/core/src/Dims.h b/compiler/enco/core/src/Dims.h
new file mode 100644
index 000000000..e0a4fd44d
--- /dev/null
+++ b/compiler/enco/core/src/Dims.h
@@ -0,0 +1,34 @@
+/*
+ * 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 __DIMS_H__
+#define __DIMS_H__
+
+#include <nncc/core/ADT/tensor/Shape.h>
+
+static inline std::vector<uint32_t> as_dims(const nncc::core::ADT::tensor::Shape &shape)
+{
+ std::vector<uint32_t> res;
+
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ res.emplace_back(shape.dim(axis));
+ }
+
+ return res;
+}
+
+#endif // __DIMS_H__
diff --git a/compiler/enco/core/src/IRUtils.cpp b/compiler/enco/core/src/IRUtils.cpp
new file mode 100644
index 000000000..59f6b0dbe
--- /dev/null
+++ b/compiler/enco/core/src/IRUtils.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "IRUtils.h"
+
+#include <cassert>
+
+namespace enco
+{
+
+/**
+ * @brief Substitute all the USE occurrences of an object with another object
+ * @param from Object to be replaced
+ * @param into Object to be used instead
+ * NOTE This maybe used when something like -- 'from' will be removed so we need
+ * to replace object Consumers that use 'from' to 'into'
+ * EXAMPLE
+ * {
+ * subst(child, bigone);
+ * m->entity()->object()->destroy(child);
+ * }
+ * This code will change all the Consumers that use 'child' to 'bigone' and
+ * destroy the 'child' object.
+ */
+void subst(coco::Object *from, coco::Object *into)
+{
+ assert(from != into);
+
+ while (!from->uses()->empty())
+ {
+ auto use = *(from->uses()->begin());
+
+ use->value(into);
+ }
+}
+
+std::vector<coco::Instr *> instr_sequence(coco::Module *m)
+{
+ std::vector<coco::Instr *> res;
+
+ for (auto B = m->block()->head(); B; B = B->next())
+ {
+ for (auto I = B->instr()->head(); I; I = I->next())
+ {
+ res.emplace_back(I);
+ }
+ }
+
+ return res;
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/IRUtils.h b/compiler/enco/core/src/IRUtils.h
new file mode 100644
index 000000000..da0754303
--- /dev/null
+++ b/compiler/enco/core/src/IRUtils.h
@@ -0,0 +1,41 @@
+/*
+ * 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 __ENCO_IR_UTILS_H__
+#define __ENCO_IR_UTILS_H__
+
+#include <coco/IR.h>
+
+#include <vector>
+
+namespace enco
+{
+
+/**
+ * @brief Replace all the "USE" of 'from' with 'into'
+ *
+ * NOTE subst(from, into) WILL NOT update 'DEF'
+ */
+void subst(coco::Object *from, coco::Object *into);
+
+/**
+ * @brief Return instructions in execution order
+ */
+std::vector<coco::Instr *> instr_sequence(coco::Module *m);
+
+} // namespace enco
+
+#endif // __ENCO_IR_UTILS_H__
diff --git a/compiler/enco/core/src/IRValidator.cpp b/compiler/enco/core/src/IRValidator.cpp
new file mode 100644
index 000000000..1337b88e4
--- /dev/null
+++ b/compiler/enco/core/src/IRValidator.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "IRValidator.h"
+
+#include <cassert>
+
+namespace enco
+{
+
+coco::FeatureShape output_shape(coco::Conv2D *conv2D)
+{
+ auto load = conv2D->arg()->asLoad();
+ assert(load);
+
+ auto ifm = load->object()->asFeature();
+ assert(ifm);
+
+ auto ker = conv2D->ker();
+ auto stride = conv2D->stride();
+ auto pad = conv2D->pad();
+
+ auto striding_width = ifm->shape().width() + pad->left() + pad->right() - ker->shape().width();
+ auto striding_height = ifm->shape().height() + pad->top() + pad->bottom() - ker->shape().height();
+
+ // Normally the formula is round(striding_width)/stride->horizontal.
+ // in coco IR, striding_width should be a multiple of stride->horizontal(), so round(...) was
+ // removed. So does striding_height.
+ assert(striding_width % stride->horizontal() == 0);
+ assert(striding_height % stride->vertical() == 0);
+
+ auto ofm_width = striding_width / stride->horizontal() + 1;
+ auto ofm_height = striding_height / stride->vertical() + 1;
+
+ return coco::FeatureShape(ifm->shape().batch(), ker->shape().count(), ofm_height, ofm_width);
+}
+
+bool validate_output_shape(Code *code)
+{
+ auto module = code->module();
+
+ // for each eval ( conv2d ( ... ) ), check the output shape of conv2D matches output of eval
+ for (auto blk = module->block()->head(); blk; blk = blk->next())
+ {
+ for (auto instr = blk->instr()->head(); instr; instr = instr->next())
+ {
+ auto eval = instr->asEval();
+ if (eval == nullptr)
+ continue;
+
+ auto op = eval->op();
+ if (!op->asConv2D())
+ continue;
+
+ auto conv2D = op->asConv2D();
+ auto expected_shape = output_shape(conv2D);
+
+ auto eval_out = eval->out()->asFeature();
+ assert(eval_out);
+
+ auto actual_shape = eval_out->shape();
+
+ if (actual_shape != expected_shape)
+ return false;
+ }
+ }
+ return true;
+}
+
+bool validate(Code *code) { return validate_output_shape(code); }
+
+} // namespace enco
diff --git a/compiler/enco/core/src/IRValidator.h b/compiler/enco/core/src/IRValidator.h
new file mode 100644
index 000000000..f4adb0a5e
--- /dev/null
+++ b/compiler/enco/core/src/IRValidator.h
@@ -0,0 +1,29 @@
+/*
+ * 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 __ENCO_IR_VALIDATOR_H__
+#define __ENCO_IR_VALIDATOR_H__
+
+#include "Code.h"
+
+namespace enco
+{
+
+bool validate(Code *code);
+
+} // namespace enco
+
+#endif // __ENCO_IR_VALIDATOR_H__
diff --git a/compiler/enco/core/src/IRValidator.test.cpp b/compiler/enco/core/src/IRValidator.test.cpp
new file mode 100644
index 000000000..14cda6173
--- /dev/null
+++ b/compiler/enco/core/src/IRValidator.test.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 "IRValidator.h"
+
+#include "Code.h"
+
+#include <gtest/gtest.h>
+
+#include <array>
+
+namespace
+{
+
+using IntList4 = std::array<int, 4>;
+using IntList2 = std::array<int, 2>;
+
+} // namespace
+
+// The layout of ifm, ker, ofm is NHWC, pad == {top, bottom, left, right}, and stride == {vertical,
+// horizontal}.
+std::unique_ptr<coco::Module> get_conv2D(IntList4 ifm, IntList4 ker, IntList4 ofm, IntList4 pad,
+ IntList2 stride)
+{
+ auto module = coco::Module::create();
+ auto block = module->entity()->block()->create();
+ auto eval = module->entity()->instr()->create<coco::Eval>();
+ auto load = module->entity()->op()->create<coco::Load>();
+ auto conv2D = module->entity()->op()->create<coco::Conv2D>();
+
+ auto ifm_obj = module->entity()->object()->create<coco::FeatureObject>();
+ coco::FeatureShape ifm_shape(ifm[0], ifm[3], ifm[1], ifm[2]);
+ ifm_obj->layout(coco::FeatureLayouts::BHWC::create(ifm_shape));
+
+ auto ofm_obj = module->entity()->object()->create<coco::FeatureObject>();
+ coco::FeatureShape ofm_shape(ofm[0], ofm[3], ofm[1], ofm[2]);
+ ofm_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape));
+
+ auto ker_obj = module->entity()->object()->create<coco::KernelObject>();
+ nncc::core::ADT::kernel::Shape ker_shape(ker[0], ker[3], ker[1], ker[2]);
+ ker_obj->layout(coco::KernelLayouts::NHWC::create(ker_shape));
+
+ // linking entities
+ module->block()->append(block);
+ block->instr()->append(eval);
+ eval->op(conv2D);
+ eval->out(ofm_obj);
+ load->object(ifm_obj);
+ conv2D->ker(ker_obj);
+ conv2D->arg(load);
+
+ // param setting
+ conv2D->pad()->top(pad[0]).bottom(pad[1]).left(pad[2]).right(pad[3]);
+ conv2D->stride()->vertical(stride[0]).horizontal(stride[1]);
+
+ return std::move(module);
+}
+
+TEST(IRValidatorTest, conv2D_simple)
+{
+ auto ifm_nhwc = IntList4{1, 3, 3, 2};
+ auto ker_nhwc = IntList4{1, 1, 1, 2};
+ auto ofm_nhwc = IntList4{1, 3, 3, 1};
+
+ auto pad_tblr = IntList4{0, 0, 0, 0};
+ auto stride_vh = IntList2{1, 1};
+
+ auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh);
+ enco::Code code{module.get(), nullptr};
+
+ ASSERT_TRUE(enco::validate(&code));
+}
+
+TEST(IRValidatorTest, conv2D_stride_2)
+{
+ auto ifm_nhwc = IntList4{1, 4, 4, 3};
+ auto ker_nhwc = IntList4{2, 2, 2, 3};
+ auto ofm_nhwc = IntList4{1, 3, 3, 2};
+
+ auto pad_tblr = IntList4{1, 1, 1, 1};
+ auto stride_vh = IntList2{2, 2};
+
+ auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh);
+ enco::Code code{module.get(), nullptr};
+
+ ASSERT_TRUE(enco::validate(&code));
+}
+
+TEST(IRValidatorTest, conv2D_output_batch_check)
+{
+ auto ifm_nhwc = IntList4{1, 2, 2, 2};
+ auto ker_nhwc = IntList4{3, 1, 1, 2}; // expected output depth is 3
+ auto ofm_nhwc = IntList4{1, 2, 2, 1}; // but 1
+
+ auto pad_tblr = IntList4{0, 0, 0, 0};
+ auto stride_vh = IntList2{1, 1};
+
+ auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh);
+ enco::Code code{module.get(), nullptr};
+
+ ASSERT_FALSE(enco::validate(&code));
+}
+
+TEST(IRValidatorTest, conv2D_wrong_HW)
+{
+ auto ifm_nhwc = IntList4{1, 2, 2, 1};
+ auto ker_nhwc = IntList4{1, 2, 2, 1};
+ auto ofm_nhwc = IntList4{1, 1, 1, 1}; // HW should be 2, 2
+
+ auto pad_tblr = IntList4{1, 1, 1, 1};
+ auto stride_vh = IntList2{2, 2};
+
+ auto module = get_conv2D(ifm_nhwc, ker_nhwc, ofm_nhwc, pad_tblr, stride_vh);
+ enco::Code code{module.get(), nullptr};
+
+ ASSERT_FALSE(enco::validate(&code));
+}
diff --git a/compiler/enco/core/src/Pass.h b/compiler/enco/core/src/Pass.h
new file mode 100644
index 000000000..d78cfaad3
--- /dev/null
+++ b/compiler/enco/core/src/Pass.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 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 __ENCO_PASS_H__
+#define __ENCO_PASS_H__
+
+#include "Session.h"
+
+#include <string>
+
+namespace enco
+{
+
+class Pass
+{
+public:
+ class Name
+ {
+ public:
+ Name(const std::string &content) : _content{content}
+ {
+ // DO NOTHING
+ }
+
+ Name(const Name &) = default;
+ Name(Name &&) = default;
+
+ ~Name() = default;
+
+ public:
+ const std::string &content(void) const { return _content; }
+
+ private:
+ std::string _content;
+ };
+
+public:
+ Pass(const Name &name) : _name{name}
+ {
+ // DO NOTHING
+ }
+
+ Pass(const Pass &) = delete;
+ Pass(Pass &&) = delete;
+
+ virtual ~Pass() = default;
+
+public:
+ const Name &name(void) const { return _name; }
+
+public:
+ virtual void run(const SessionID &) const = 0;
+
+private:
+ Name _name;
+};
+
+static inline Pass::Name pass_name(const std::string &name) { return Pass::Name{name}; }
+
+} // namespace enco
+
+#define PASS_CTOR(NAME) \
+ NAME() : enco::Pass { enco::pass_name(#NAME) }
+
+#endif // __ENCO_PASS_H__
diff --git a/compiler/enco/core/src/Pass.test.cpp b/compiler/enco/core/src/Pass.test.cpp
new file mode 100644
index 000000000..112bd7478
--- /dev/null
+++ b/compiler/enco/core/src/Pass.test.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019 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 "Pass.h"
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+struct ExamplePass final : public enco::Pass
+{
+ PASS_CTOR(ExamplePass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const enco::SessionID &) const override { return; }
+};
+
+} // namespace
+
+TEST(PASS, ctor)
+{
+ ExamplePass pass;
+
+ ASSERT_EQ(pass.name().content(), "ExamplePass");
+}
diff --git a/compiler/enco/core/src/Pipeline.h b/compiler/enco/core/src/Pipeline.h
new file mode 100644
index 000000000..8ab43c16a
--- /dev/null
+++ b/compiler/enco/core/src/Pipeline.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 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 __ENCO_PIPELINE_H__
+#define __ENCO_PIPELINE_H__
+
+#include "Pass.h"
+
+#include <memory>
+#include <vector>
+#include <cstdint>
+
+namespace enco
+{
+
+class Pipeline
+{
+public:
+ uint32_t size(void) const { return _passes.size(); }
+
+public:
+ const Pass &at(uint32_t n) const { return *(_passes.at(n)); }
+
+public:
+ void append(std::unique_ptr<Pass> &&pass) { _passes.emplace_back(std::move(pass)); }
+
+private:
+ std::vector<std::unique_ptr<Pass>> _passes;
+};
+
+} // namespace enco
+
+#endif // __ENCO_PIPELINE_H__
diff --git a/compiler/enco/core/src/Pipeline.test.cpp b/compiler/enco/core/src/Pipeline.test.cpp
new file mode 100644
index 000000000..1cd730e98
--- /dev/null
+++ b/compiler/enco/core/src/Pipeline.test.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 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 "Pipeline.h"
+
+#include <gtest/gtest.h>
+
+TEST(PIPELINE, default_ctor)
+{
+ enco::Pipeline pipeline;
+
+ ASSERT_EQ(pipeline.size(), 0);
+}
diff --git a/compiler/enco/core/src/Session.cpp b/compiler/enco/core/src/Session.cpp
new file mode 100644
index 000000000..034f23892
--- /dev/null
+++ b/compiler/enco/core/src/Session.cpp
@@ -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.
+ */
+
+#include "Session.h"
+
+#include <stdex/Memory.h>
+
+#include <map>
+#include <memory>
+
+using stdex::make_unique;
+
+namespace
+{
+
+std::map<enco::SessionID, std::unique_ptr<enco::Code>> sess_to_code;
+std::map<const coco::Module *, enco::SessionID> module_to_sess;
+std::map<const coco::Data *, enco::SessionID> data_to_sess;
+
+} // namespace
+
+namespace enco
+{
+
+SessionID make_session(coco::Module *m, coco::Data *d)
+{
+ static uint32_t sess = 0;
+ SessionID curr{sess++};
+
+ sess_to_code[curr] = make_unique<Code>(m, d);
+ module_to_sess[m] = curr;
+ data_to_sess[d] = curr;
+
+ return curr;
+}
+
+SessionID session(const coco::Module *m) { return module_to_sess.at(m); }
+SessionID session(const coco::Data *d) { return data_to_sess.at(d); }
+
+coco::Module *module(const SessionID &sess) { return sess_to_code.at(sess)->module(); }
+coco::Data *data(const SessionID &sess) { return sess_to_code.at(sess)->data(); }
+
+Code *code(const SessionID &sess) { return sess_to_code.at(sess).get(); }
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Session.h b/compiler/enco/core/src/Session.h
new file mode 100644
index 000000000..b6d502f3b
--- /dev/null
+++ b/compiler/enco/core/src/Session.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 __ENCO_SESSION_H__
+#define __ENCO_SESSION_H__
+
+#include "Code.h"
+
+namespace enco
+{
+
+// TODO Rewrite this definition
+using SessionID = uint32_t;
+
+SessionID make_session(coco::Module *m, coco::Data *d);
+
+SessionID session(const coco::Module *m);
+SessionID session(const coco::Data *d);
+
+coco::Module *module(const SessionID &);
+coco::Data *data(const SessionID &);
+
+static inline coco::Module *module(const coco::Data *d) { return module(session(d)); }
+static inline coco::Data *data(const coco::Module *m) { return data(session(m)); }
+
+// WARN This API is introduced just for backward compatibility
+// Do NOT use this anymore as it will be removed
+Code *code(const SessionID &);
+
+} // namespace enco
+
+#endif // __ENCO_SESSION_H__
diff --git a/compiler/enco/core/src/String.h b/compiler/enco/core/src/String.h
new file mode 100644
index 000000000..0f04f1ffe
--- /dev/null
+++ b/compiler/enco/core/src/String.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 __ENCO_STRING_H__
+#define __ENCO_STRING_H__
+
+//
+// String-manipulating routines
+//
+#include <ostream>
+#include <sstream>
+
+#include <string>
+
+namespace enco
+{
+
+template <typename It> void concat(std::ostream &os, const std::string &sep, It beg, It end)
+{
+ uint32_t count = 0;
+
+ for (auto it = beg; it != end; ++it, ++count)
+ {
+ if (count == 0)
+ {
+ os << *it;
+ }
+ else
+ {
+ os << sep << *it;
+ }
+ }
+}
+
+template <typename It> std::string concat(const std::string &sep, It beg, It end)
+{
+ std::stringstream ss;
+ concat(ss, sep, beg, end);
+ return ss.str();
+}
+
+} // namespace enco
+
+#endif // __ENCO_STRING_H__
diff --git a/compiler/enco/core/src/Support/Debugging.cpp b/compiler/enco/core/src/Support/Debugging.cpp
new file mode 100644
index 000000000..bd65a27d8
--- /dev/null
+++ b/compiler/enco/core/src/Support/Debugging.cpp
@@ -0,0 +1,533 @@
+/*
+ * 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 "Debugging.h"
+
+#include <pp/LinearDocument.h>
+#include <pp/MultiLineTextUtils.h>
+
+#include <stack>
+
+#include <iostream>
+
+#define DEBUGGING_API_P(NAME, TYPE, VAR) \
+ static void _##NAME(const TYPE *); \
+ void NAME(long p) { NAME(reinterpret_cast<const TYPE *>(p)); } \
+ void NAME(const TYPE *p) \
+ { \
+ if (p == nullptr) \
+ { \
+ std::cout << "(nullptr)" << std::endl; \
+ } \
+ else \
+ { \
+ _##NAME(p); \
+ } \
+ } \
+ void _##NAME(const TYPE *VAR)
+
+namespace
+{
+
+class SectionBuilder
+{
+public:
+ SectionBuilder(const std::string &tag) : _tag{tag}
+ {
+ // DO NOTHING
+ }
+
+public:
+ template <typename Callback> pp::LinearDocument build(Callback cb) const
+ {
+ pp::LinearDocument res;
+
+ res.append(_tag, " {");
+ res.indent();
+
+ cb(res);
+
+ res.unindent();
+ res.append("}");
+
+ return res;
+ }
+
+private:
+ std::string _tag;
+};
+
+template <typename Callback>
+pp::LinearDocument operator<<(const SectionBuilder &builder, Callback cb)
+{
+ return builder.build(std::forward<Callback>(cb));
+}
+
+SectionBuilder section(const std::string &tag) { return SectionBuilder{tag}; }
+}
+
+/**
+ * SECTION: Bag
+ */
+namespace
+{
+
+pp::LinearDocument describe(const coco::Bag *bag)
+{
+ pp::LinearDocument doc;
+
+ doc.append("addr: ", bag);
+ doc.append("size: ", bag->size());
+ // TODO Print Read
+ // TODO Print Update
+ // TODO Print Dep
+ return doc;
+}
+
+} // namespace
+
+DEBUGGING_API_P(enco_dump_all_bags, coco::Module, m)
+{
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+ assert(bag != nullptr);
+
+ auto set = [bag](pp::LinearDocument &doc) { doc.append(describe(bag)); };
+ auto desc = section("bag").build(set);
+
+ std::cout << desc << std::endl;
+ }
+}
+
+/**
+ * SECTION: Object
+ */
+namespace
+{
+std::string op_kind(const coco::Op *op);
+
+/**
+ * @brief Return the def(producer) type of object
+ */
+std::string def_kind(const coco::Def *def)
+{
+ if (def)
+ {
+ if (auto instr = dynamic_cast<coco::Instr *>(def->producer()))
+ {
+ std::stringstream ss;
+
+ if (auto eval = instr->asEval())
+ {
+ ss << op_kind(eval->op()) << "(" << instr << ")";
+ return ss.str();
+ }
+ else if (instr->asCopy())
+ {
+ ss << "Copy(" << instr << ")";
+ return ss.str();
+ }
+ else if (instr->asShuffle())
+ {
+ ss << "Shuffle(" << instr << ")";
+ return ss.str();
+ }
+ }
+ else
+ {
+ return "(unknown)";
+ }
+ }
+
+ return "(none)";
+}
+
+pp::LinearDocument describe(const coco::Object *obj)
+{
+ pp::LinearDocument doc;
+
+ doc.append("addr: ", obj);
+ doc.append("bag: ", obj->bag());
+ doc.append("producer: ", def_kind(obj->def()));
+ // TODO Show Uses
+ // TODO Show FeatureObject/KernelObect info
+
+ return doc;
+}
+
+} // namespace
+
+DEBUGGING_API_P(enco_dump_all_objects, coco::Module, m)
+{
+ for (uint32_t n = 0; n < m->entity()->object()->size(); ++n)
+ {
+ auto obj = m->entity()->object()->at(n);
+ assert(obj != nullptr);
+
+ auto set = [obj](pp::LinearDocument &doc) { doc.append(describe(obj)); };
+ auto desc = section("object").build(set);
+
+ std::cout << desc << std::endl;
+ }
+}
+
+/**
+ * SECTION: Op
+ */
+namespace
+{
+
+struct OpTree
+{
+public:
+ OpTree(const coco::Op *op) : _op{op}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const coco::Op *root(void) const { return _op; }
+
+private:
+ const coco::Op *_op;
+};
+
+std::string op_kind(const coco::Op *op)
+{
+ struct OpKind : public coco::Op::Visitor<std::string>
+ {
+ std::string visit(const coco::Load *) override { return "Load"; }
+ std::string visit(const coco::Conv2D *) override { return "Conv2D"; }
+ std::string visit(const coco::MaxPool2D *) override { return "MaxPool2D"; }
+ std::string visit(const coco::AvgPool2D *) override { return "AvgPool2D"; }
+ std::string visit(const coco::PadF *) override { return "PadF"; }
+ std::string visit(const coco::ReLU *) override { return "ReLU"; }
+ std::string visit(const coco::Add *) override { return "Add"; }
+ std::string visit(const coco::Mul *) override { return "Mul"; }
+ std::string visit(const coco::ConcatF *) override { return "ConcatF"; }
+ std::string visit(const coco::Sub *) override { return "Sub"; }
+ std::string visit(const coco::Sqrt *) override { return "Sqrt"; }
+ std::string visit(const coco::Div *) override { return "Div"; }
+ };
+
+ OpKind v;
+
+ return op->accept(v);
+}
+
+pp::LinearDocument describe(const coco::Padding2D *pad)
+{
+ pp::LinearDocument doc;
+
+ doc.append("top: ", pad->top());
+ doc.append("bottom: ", pad->bottom());
+ doc.append("left: ", pad->left());
+ doc.append("right: ", pad->right());
+
+ return doc;
+}
+
+pp::LinearDocument describe(const coco::Stride2D *stride)
+{
+ pp::LinearDocument doc;
+
+ doc.append("vertical: ", stride->vertical());
+ doc.append("horizontal ", stride->horizontal());
+
+ return doc;
+}
+
+pp::LinearDocument describe(const coco::Conv2D *conv)
+{
+ pp::LinearDocument doc;
+
+ doc.append("arg: ", conv->arg());
+ doc.append("ker: ", conv->ker());
+ doc.append("group: ", conv->group());
+
+ if (auto pad = conv->pad())
+ {
+ auto set = [pad](pp::LinearDocument &doc) { doc.append(describe(pad)); };
+ auto desc = section("pad").build(set);
+ doc.append(desc);
+ }
+
+ if (auto stride = conv->stride())
+ {
+ auto set = [stride](pp::LinearDocument &doc) { doc.append(describe(stride)); };
+ auto desc = section("stride").build(set);
+ doc.append(desc);
+ }
+
+ return doc;
+}
+
+pp::LinearDocument describe(const coco::Op *op)
+{
+ pp::LinearDocument doc;
+
+ doc.append("addr: ", op);
+ doc.append("kind: ", op_kind(op));
+ doc.append("parent(instr): ", op->parent());
+ doc.append("up(op): ", op->up());
+
+ if (auto conv = op->asConv2D())
+ {
+ auto set = [conv](pp::LinearDocument &doc) { doc.append(describe(conv)); };
+ auto desc = section("conv2d").build(set);
+ doc.append(desc);
+ }
+ else if (auto load = op->asLoad())
+ {
+ auto set = [load](pp::LinearDocument &doc) { doc.append(describe(load->object())); };
+ auto desc = section("load").build(set);
+ doc.append(desc);
+ }
+
+ return doc;
+}
+
+pp::LinearDocument describe(const OpTree &t, bool verbose = false)
+{
+ pp::LinearDocument doc;
+
+ struct Frame
+ {
+ public:
+ Frame(const coco::Op *op) : _op{op}, _indicator{0}
+ {
+ // op SHOULD BE valid
+ assert(_op != nullptr);
+ }
+
+ public:
+ /**
+ * @brief Return a pointer to coco::Op of interest
+ */
+ const coco::Op *op(void) const { return _op; }
+
+ /**
+ * @brief Return the indicator
+ *
+ * Let's assume that the arity of a coco::Op of interest is N
+ * INDICATOR 0 -> Print the op itself
+ * INDICATOR 1 -> Print the first argument
+ * ...
+ * INDICATOR N -> Print the N-th argument
+ * INDICATOR N + 1 -> Done
+ */
+ uint32_t indicator(void) const { return _indicator; }
+
+ public:
+ void advance(void) { _indicator += 1; }
+
+ private:
+ const coco::Op *_op;
+ uint32_t _indicator;
+ };
+
+ std::stack<Frame> stack;
+
+ stack.emplace(t.root());
+
+ while (stack.size() > 0)
+ {
+ auto op = stack.top().op();
+ uint32_t indicator = stack.top().indicator();
+
+ if (indicator == 0)
+ {
+ doc.append(op_kind(op), " (", op, ")");
+
+ doc.indent();
+ stack.top().advance();
+
+ // TODO Need to update it to better design for verbose flag
+ if (verbose)
+ {
+ auto set = [op](pp::LinearDocument &doc) { doc.append(describe(op)); };
+ auto desc = section("op").build(set);
+ doc.append(desc);
+ }
+ }
+ else if (indicator < op->arity() + 1)
+ {
+ stack.top().advance();
+ stack.emplace(op->arg(indicator - 1));
+ }
+ else
+ {
+ assert(indicator == op->arity() + 1);
+ doc.unindent();
+ stack.pop();
+ }
+ }
+
+ return doc;
+}
+
+} // namespace
+
+DEBUGGING_API_P(enco_dump_op, coco::Op, op)
+{
+ {
+ std::cout << describe(op) << std::endl;
+ }
+}
+
+DEBUGGING_API_P(enco_dump_op_tree, coco::Op, op)
+{
+ {
+ std::cout << describe(OpTree(op)) << std::endl;
+ }
+}
+
+DEBUGGING_API_P(enco_dump_all_ops, coco::Module, m)
+{
+ SectionBuilder section_builder{"op"};
+
+ for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
+ {
+ auto op = m->entity()->op()->at(n);
+ assert(op != nullptr);
+
+ auto desc = section("op").build([op](pp::LinearDocument &doc) { doc.append(describe(op)); });
+
+ std::cout << desc << std::endl;
+ }
+}
+
+/**
+ * SECTION: Instr
+ */
+namespace
+{
+
+std::string kind(const coco::Instr *ins)
+{
+ struct InstrKind : public coco::Instr::Visitor<std::string>
+ {
+ std::string visit(const coco::Eval *) override { return "Eval"; }
+ std::string visit(const coco::Copy *) override { return "Copy"; }
+ std::string visit(const coco::Shuffle *) override { return "Shuffle"; }
+ };
+
+ InstrKind v;
+
+ return ins->accept(v);
+}
+
+pp::LinearDocument describe(const coco::Instr *ins, bool verbose = false)
+{
+ pp::LinearDocument doc;
+
+ doc.append("addr: ", ins);
+ doc.append("kind: ", kind(ins));
+ doc.append("parent: ", ins->parent());
+
+ // TODO Need to update it to better design for verbose flag
+ if (verbose)
+ {
+ if (auto eval = ins->asEval())
+ {
+ auto optset = [eval, verbose](pp::LinearDocument &doc) {
+ doc.append(describe(OpTree(eval->op()), verbose));
+ };
+ auto optdesc = section("op").build(optset);
+ doc.append(optdesc);
+
+ auto outset = [eval](pp::LinearDocument &doc) { doc.append(describe(eval->out())); };
+ auto outdesc = section("out").build(outset);
+ doc.append(outdesc);
+ }
+ else if (auto copy = ins->asCopy())
+ {
+ auto from = [copy](pp::LinearDocument &doc) { doc.append(describe(copy->from())); };
+ auto into = [copy](pp::LinearDocument &doc) { doc.append(describe(copy->into())); };
+
+ auto fdesc = section("from").build(from);
+ doc.append(fdesc);
+
+ auto idesc = section("into").build(into);
+ doc.append(idesc);
+ }
+ }
+
+ return doc;
+}
+
+} // namespace
+
+DEBUGGING_API_P(enco_dump_all_instrs, coco::Module, m)
+{
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ auto ins = m->entity()->instr()->at(n);
+ assert(ins != nullptr);
+
+ auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins)); };
+ auto desc = section("instr").build(setter);
+
+ std::cout << desc << std::endl;
+ }
+}
+
+DEBUGGING_API_P(enco_dump_all_instrs_v, coco::Module, m)
+{
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ auto ins = m->entity()->instr()->at(n);
+ assert(ins != nullptr);
+
+ auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins, true)); };
+ auto desc = section("instr").build(setter);
+
+ std::cout << desc << std::endl;
+ }
+}
+
+DEBUGGING_API_P(enco_dump_instr, coco::Instr, ins)
+{
+ auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins, true)); };
+ auto desc = section("instr").build(setter);
+
+ std::cout << desc << std::endl;
+}
+
+/**
+ * SECTION: Block
+ */
+namespace
+{
+
+pp::LinearDocument describe(const coco::Block *blk)
+{
+ pp::LinearDocument doc;
+
+ for (auto ins = blk->instr()->head(); ins; ins = ins->next())
+ {
+ auto setter = [ins](pp::LinearDocument &doc) { doc.append(describe(ins)); };
+ auto desc = section("instr").build(setter);
+ doc.append(desc);
+ }
+
+ return doc;
+}
+
+} // namespace
+
+DEBUGGING_API_P(enco_dump_block, coco::Block, blk) { std::cout << describe(blk) << std::endl; }
diff --git a/compiler/enco/core/src/Support/Debugging.h b/compiler/enco/core/src/Support/Debugging.h
new file mode 100644
index 000000000..c28356e76
--- /dev/null
+++ b/compiler/enco/core/src/Support/Debugging.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.
+ */
+
+/**
+ * @file Debugging.h
+ * @brief This file includes various interactive debugging helpers
+ */
+
+#ifndef __ENCO_SUPPORT_DEBUGGING_H__
+#define __ENCO_SUPPORT_DEBUGGING_H__
+
+#include <coco/IR.h>
+
+static_assert(sizeof(long) == sizeof(void *), "sizeof(long) == sizeof(pointer)");
+
+/**
+ * Debugging API with a single pointer argument
+ */
+#define DEBUGGING_API_P(NAME, TYPE) \
+ void NAME(const TYPE *); \
+ void NAME(long);
+
+/**
+ * Print the details of all the allocated coco::Bag in coco::Module
+ *
+ * (gdb) call enco_dump_all_bags(bag->module())
+ * (gdb) call enco_dump_all_bags(0x...)
+ */
+DEBUGGING_API_P(enco_dump_all_bags, coco::Module);
+
+/**
+ * Print the details of all the allocated coco::Object in coco::Module
+ *
+ * (gdb) call enco_dump_all_objects(obj->module())
+ * (gdb) call enco_dump_all_objects(0x...)
+ */
+DEBUGGING_API_P(enco_dump_all_objects, coco::Module);
+
+/**
+ * Print the details of coco::Op
+ *
+ * (gdb) call enco_dump_op(op)
+ * (gdb) call enco_dump_op(0x....)
+ */
+DEBUGGING_API_P(enco_dump_op, coco::Op);
+
+/**
+ * Print the (simplified) tree layout of coco::Op
+ *
+ * (gdb) call enco_dump_op_tree(op)
+ * (gdb) call enco_dump_op_tree(0x....)
+ */
+DEBUGGING_API_P(enco_dump_op_tree, coco::Op);
+
+/**
+ * Print the details of all the allocated coco::Op in coco::Module
+ *
+ * (gdb) call enco_dump_all_ops(op->module())
+ * (gdb) call enco_dump_all_ops(0x....)
+ */
+DEBUGGING_API_P(enco_dump_all_ops, coco::Module);
+
+/**
+ * Print the details of all the allocated coco::Instr in coco::Module
+ *
+ * (gdb) call enco_dump_all_instrs(instr->module())
+ * (gdb) call enco_dump_all_instrs(0x...)
+ */
+DEBUGGING_API_P(enco_dump_all_instrs, coco::Module);
+
+/**
+ * Print the more details of all the allocated coco::Instr in coco::Module
+ *
+ * (gdb) call enco_dump_all_instrs_v(instr->module())
+ * (gdb) call enco_dump_all_instrs_v(0x...)
+ */
+DEBUGGING_API_P(enco_dump_all_instrs_v, coco::Module);
+
+/**
+ * Print the details of a given coco::Instr
+ *
+ * (gdb) call enco_dump_instr(instr)
+ * (gdb) call enco_dump_instr(0x...)
+ */
+DEBUGGING_API_P(enco_dump_instr, coco::Instr);
+
+/**
+ * Print the details of all the instruction in a given block
+ *
+ * (gdb) call enco_dump_block(b)
+ * (gdb) call enco_dump_block(0x...)
+ */
+DEBUGGING_API_P(enco_dump_block, coco::Block);
+
+#undef DEBUGGING_API_P
+
+#endif // __ENCO_SUPPORT_DEBUGGING_H__
diff --git a/compiler/enco/core/src/Support/Debugging.test.cpp b/compiler/enco/core/src/Support/Debugging.test.cpp
new file mode 100644
index 000000000..49a2ad162
--- /dev/null
+++ b/compiler/enco/core/src/Support/Debugging.test.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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 "Debugging.h"
+
+#include <gtest/gtest.h>
+
+// This test aims to check whether debugging API is actually defined
+TEST(DebuggingTest, defined)
+{
+ enco_dump_op(nullptr);
+ enco_dump_all_ops(nullptr);
+}
diff --git a/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp
new file mode 100644
index 000000000..17502fb1f
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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 "AvgPoolLowering.h"
+#include "IRUtils.h"
+
+#include <coco/IR/FeatureLayouts.h>
+
+#include <nncc/core/ADT/feature/Shape.h>
+#include <nncc/core/ADT/feature/HWCLayout.h>
+
+#include <set>
+#include <cassert>
+
+using namespace nncc::core::ADT;
+using nncc::core::ADT::feature::num_elements;
+
+namespace
+{
+
+bool empty(coco::Padding2D *pad)
+{
+ return (pad->top() == 0) && (pad->bottom() == 0) && (pad->left() == 0) && (pad->right() == 0);
+}
+
+/**
+ * @brief Return a set of AvgPool2D operations (in Eval instruction) that SHOULD be lowered
+ */
+std::set<coco::AvgPool2D *> candidates(coco::Module *m)
+{
+ std::set<coco::AvgPool2D *> res;
+
+ for (auto I : enco::instr_sequence(m))
+ {
+ if (auto eval = I->asEval())
+ {
+ if (auto avgpool = eval->op()->asAvgPool2D())
+ {
+ /* Originally it was preferred to use `auto load = avgpool->arg()->asLoad()' for
+ * consitent style with other if statements.
+ * Someone may think compiler will be happy because `load` in `if` statement can
+ * be considered as a use, however, it turend out that it is not the case.
+ */
+ if (avgpool->arg()->asLoad())
+ {
+ if (avgpool->divisor() == coco::AvgPool2D::Divisor::Static)
+ {
+ res.insert(avgpool);
+ }
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace
+{
+namespace ShapeTransform
+{
+
+class Pad
+{
+public:
+ Pad(const coco::Padding2D *pad) : _pad{pad}
+ {
+ // DO NOTHING
+ }
+
+public:
+ /// @brief Return the expected OFM shape for a given IFM shape
+ feature::Shape forward(const feature::Shape &ifm_shape) const
+ {
+ const uint32_t OFM_C = ifm_shape.depth();
+ const uint32_t OFM_H = ifm_shape.height() + _pad->top() + _pad->bottom();
+ const uint32_t OFM_W = ifm_shape.width() + _pad->left() + _pad->right();
+
+ return feature::Shape{OFM_C, OFM_H, OFM_W};
+ }
+
+private:
+ const coco::Padding2D *_pad;
+};
+
+} // namespace ShapeTransform
+
+ShapeTransform::Pad shape_xform(const coco::Padding2D *pad) { return ShapeTransform::Pad{pad}; }
+
+} // namespace
+
+namespace
+{
+
+class PadInstrBuilder final
+{
+public:
+ PadInstrBuilder(const coco::Padding2D *pad) : _pad{pad}
+ {
+ // DO NOTHING
+ }
+
+public:
+ coco::Instr *build(coco::FeatureObject *ifm_obj, coco::FeatureObject *ofm_obj) const
+ {
+ assert(ifm_obj->module() == ofm_obj->module());
+ auto m = ifm_obj->module();
+ assert(m != nullptr);
+
+ auto load_op = m->entity()->op()->create<coco::Load>();
+
+ load_op->object(ifm_obj);
+
+ auto pad_op = m->entity()->op()->create<coco::PadF>();
+
+ pad_op->arg(load_op);
+
+ pad_op->pad()->top(_pad->top());
+ pad_op->pad()->bottom(_pad->bottom());
+ pad_op->pad()->left(_pad->left());
+ pad_op->pad()->right(_pad->right());
+
+ auto pad_instr = m->entity()->instr()->create<coco::Eval>();
+
+ pad_instr->out(ofm_obj);
+ pad_instr->op(pad_op);
+
+ return pad_instr;
+ }
+
+private:
+ const coco::Padding2D *_pad;
+};
+
+PadInstrBuilder pad_instr_builder(const coco::Padding2D *pad) { return PadInstrBuilder{pad}; }
+
+} // namespace
+
+namespace
+{
+
+class AvgPoolRewritePass
+{
+private:
+ void runOnModule(coco::Module *m) const;
+
+public:
+ void runOnCode(enco::Code *) const;
+};
+
+void AvgPoolRewritePass::runOnModule(coco::Module *m) const
+{
+ // Lower AvgPool2D op that resides in Eval instruction
+ for (auto avgpool : candidates(m))
+ {
+ auto ins = avgpool->parent();
+ auto load = avgpool->arg()->asLoad();
+
+ assert(ins != nullptr);
+ assert(load != nullptr);
+ assert(avgpool->divisor() == coco::AvgPool2D::Divisor::Static);
+
+ if (empty(avgpool->pad()))
+ {
+ // NOTE If there is no padding, Static and PaddingExcluded schemes are equivalent
+ avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded);
+ }
+ else
+ {
+ // Before: Static AvgPool2D with Padding
+ // After: PadF; PaddingExcluded AvgPool2D without Padding
+
+ // Create PadF
+ auto ifm_obj = load->object()->asFeature();
+ assert(ifm_obj != nullptr);
+
+ auto pad_shape = shape_xform(avgpool->pad()).forward(ifm_obj->shape());
+ auto pad_bag = m->entity()->bag()->create(num_elements(pad_shape));
+ auto pad_obj = m->entity()->object()->create<coco::FeatureObject>();
+
+ pad_obj->bag(pad_bag);
+ pad_obj->layout(coco::FeatureLayouts::BHWC::create(pad_shape));
+
+ auto pad_instr = pad_instr_builder(avgpool->pad()).build(ifm_obj, pad_obj);
+
+ // Insert PadF before AvgPool2D
+ pad_instr->insertBefore(ins);
+
+ // Rewrite AvgPool2D as PaddingExcluded AvgPool2D without Padding
+ load->object(pad_obj);
+
+ avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded);
+ avgpool->pad()->top(0);
+ avgpool->pad()->bottom(0);
+ avgpool->pad()->left(0);
+ avgpool->pad()->right(0);
+ }
+ }
+}
+
+void AvgPoolRewritePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
+
+} // namespace
+
+namespace enco
+{
+
+void lower_avgpool(enco::Code *code)
+{
+ AvgPoolRewritePass pass;
+ pass.runOnCode(code);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/AvgPoolLowering.h b/compiler/enco/core/src/Transforms/AvgPoolLowering.h
new file mode 100644
index 000000000..71a5253df
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/AvgPoolLowering.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __REWRITE_H__
+#define __REWRITE_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Rewrite NN API-incompatible average pooling
+ */
+void lower_avgpool(enco::Code *);
+
+struct AvgPoolLoweringPass final : public Pass
+{
+ PASS_CTOR(AvgPoolLoweringPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { lower_avgpool(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __REWRITE_H__
diff --git a/compiler/enco/core/src/Transforms/ConcatLowering.cpp b/compiler/enco/core/src/Transforms/ConcatLowering.cpp
new file mode 100644
index 000000000..bf613c983
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/ConcatLowering.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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 "CopyLowering.h"
+#include "IRUtils.h"
+
+#include <nncc/core/ADT/tensor/IndexEnumerator.h>
+
+#include <set>
+#include <cassert>
+
+using namespace nncc::core::ADT;
+
+namespace
+{
+
+inline uint32_t as_tensor_axis(const coco::ConcatF::Axis &axis)
+{
+ switch (axis)
+ {
+ case coco::ConcatF::Axis::Batch:
+ return 0;
+ case coco::ConcatF::Axis::Depth:
+ return 1;
+ case coco::ConcatF::Axis::Height:
+ return 2;
+ case coco::ConcatF::Axis::Width:
+ return 3;
+ default:
+ break;
+ };
+
+ throw std::invalid_argument{"axis is unknown value"};
+}
+
+tensor::Shape as_tensor_shape(const coco::FeatureLayout *l)
+{
+ assert(l != nullptr);
+
+ tensor::Shape res;
+
+ res.resize(4);
+
+ res.dim(as_tensor_axis(coco::ConcatF::Axis::Batch)) = l->batch();
+ res.dim(as_tensor_axis(coco::ConcatF::Axis::Depth)) = l->depth();
+ res.dim(as_tensor_axis(coco::ConcatF::Axis::Height)) = l->height();
+ res.dim(as_tensor_axis(coco::ConcatF::Axis::Width)) = l->width();
+
+ return res;
+}
+
+coco::ElemID as_element_index(const coco::FeatureLayout *l, const tensor::Index &idx)
+{
+ assert(l != nullptr);
+ assert(idx.rank() == 4);
+
+ const auto b = idx.at(as_tensor_axis(coco::ConcatF::Axis::Batch));
+ const auto ch = idx.at(as_tensor_axis(coco::ConcatF::Axis::Depth));
+ const auto row = idx.at(as_tensor_axis(coco::ConcatF::Axis::Height));
+ const auto col = idx.at(as_tensor_axis(coco::ConcatF::Axis::Width));
+
+ return l->at(b, ch, row, col);
+}
+
+std::set<coco::Eval *> candidates(coco::Module *m)
+{
+ std::set<coco::Eval *> res;
+
+ for (auto ins : enco::instr_sequence(m))
+ {
+ if (auto eval = ins->asEval())
+ {
+ if (eval->op()->asConcatF())
+ {
+ res.insert(eval);
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void lower_concat(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (auto eval : candidates(m))
+ {
+ auto concat_f = eval->op()->asConcatF();
+ assert(concat_f != nullptr);
+
+ auto left_feature = concat_f->left()->asLoad()->object()->asFeature();
+ assert(left_feature != nullptr);
+ auto left_shape = as_tensor_shape(left_feature->layout());
+
+ auto right_feature = concat_f->right()->asLoad()->object()->asFeature();
+ assert(right_feature != nullptr);
+ auto right_shape = as_tensor_shape(right_feature->layout());
+
+ auto out_feature = eval->out()->asFeature();
+ assert(out_feature != nullptr);
+ auto out_shape = as_tensor_shape(out_feature->layout());
+
+ auto concat_axe = as_tensor_axis(concat_f->axis());
+
+ // Lower: Left -> Output
+ {
+ auto src_feature = left_feature;
+ auto src_shape = left_shape;
+
+ auto ins = m->entity()->instr()->create<coco::Shuffle>();
+
+ assert(src_feature->bag() != nullptr);
+ assert(out_feature->bag() != nullptr);
+
+ ins->from(src_feature->bag());
+ ins->into(out_feature->bag());
+
+ for (tensor::IndexEnumerator e{src_shape}; e.valid(); e.advance())
+ {
+ tensor::Index src_index = e.current();
+ tensor::Index out_index = e.current();
+
+ auto from = as_element_index(src_feature->layout(), src_index);
+ auto into = as_element_index(out_feature->layout(), out_index);
+
+ ins->insert(from, into);
+ }
+
+ ins->insertAfter(eval);
+ }
+
+ // Lower: Right -> Output
+ {
+ auto src_feature = right_feature;
+ auto src_shape = right_shape;
+
+ auto ins = m->entity()->instr()->create<coco::Shuffle>();
+
+ assert(src_feature->bag() != nullptr);
+ assert(out_feature->bag() != nullptr);
+
+ ins->from(src_feature->bag());
+ ins->into(out_feature->bag());
+
+ for (tensor::IndexEnumerator e{src_shape}; e.valid(); e.advance())
+ {
+ tensor::Index src_index = e.current();
+ tensor::Index out_index = e.current();
+
+ out_index.at(concat_axe) = out_index.at(concat_axe) + left_shape.dim(concat_axe);
+
+ auto from = as_element_index(src_feature->layout(), src_index);
+ auto into = as_element_index(out_feature->layout(), out_index);
+
+ ins->insert(from, into);
+ }
+
+ ins->insertAfter(eval);
+ }
+
+ // Unlink "Eval" and "ConcatF" op tree
+ eval->op(nullptr);
+
+ // Delete "Concat" op tree
+ m->entity()->op()->destroy(concat_f->left());
+ m->entity()->op()->destroy(concat_f->right());
+ m->entity()->op()->destroy(concat_f);
+
+ // Deatch "Eval" instruction from the block
+ eval->detach();
+
+ // Delete "Eval" instruction
+ m->entity()->instr()->destroy(eval);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/ConcatLowering.h b/compiler/enco/core/src/Transforms/ConcatLowering.h
new file mode 100644
index 000000000..5d20e627b
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/ConcatLowering.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __ENCO_CONCAT_LOWERING_H__
+#define __ENCO_CONCAT_LOWERING_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Lower eval(Concat(...)) as a sequence of shuffle instructions
+ */
+void lower_concat(enco::Code *code);
+
+struct ConcatLoweringPass final : public Pass
+{
+ PASS_CTOR(ConcatLoweringPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { lower_concat(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_CONCAT_LOWERING_H__
diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.cpp b/compiler/enco/core/src/Transforms/ConstantFolding.cpp
new file mode 100644
index 000000000..cd6f22351
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/ConstantFolding.cpp
@@ -0,0 +1,442 @@
+/*
+ * 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 "ConstantFolding.h"
+#include "Session.h"
+
+#include <queue>
+#include <cmath>
+#include <cassert>
+
+namespace
+{
+
+/**
+ * @brief is_constant_bag(b) returns true if the bag "b" has corresponding weight
+ */
+bool is_constant_bag(coco::Bag *b)
+{
+ auto m = b->module();
+ auto d = enco::data(m);
+ return d->allocated(b);
+}
+
+class ConstantBagEnumerator
+{
+public:
+ ConstantBagEnumerator(enco::Code *code) : _code{code}
+ {
+ // DO NOTHING
+ }
+
+public:
+ template <typename Callable> void enumerate(Callable cb) const
+ {
+ auto m = _code->module();
+
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto b = m->entity()->bag()->at(n);
+
+ if (is_constant_bag(b))
+ {
+ cb(b);
+ }
+ }
+ }
+
+private:
+ enco::Code *_code;
+};
+
+template <typename Callable> void operator<<(const ConstantBagEnumerator &e, Callable &&cb)
+{
+ e.enumerate(std::forward<Callable>(cb));
+}
+
+ConstantBagEnumerator constant_bag_enumerator(enco::Code *code)
+{
+ return ConstantBagEnumerator{code};
+}
+
+} // namespace
+
+namespace
+{
+
+/**
+ * @brief Take the first element from the queue
+ * @note The queue SHOULD have at least one element.
+ */
+template <typename T> T take(std::queue<T> &q)
+{
+ assert(q.size() > 0);
+ auto res = q.front();
+ q.pop();
+ return res;
+}
+
+} // namespace
+
+namespace
+{
+
+void fold_constant(std::queue<coco::Bag *> &q, coco::Copy *copy)
+{
+ auto m = copy->module();
+ auto d = enco::data(m);
+
+ auto src_obj = copy->from();
+ auto src_bag = src_obj->bag();
+
+ auto dst_obj = copy->into();
+ auto dst_bag = dst_obj->bag();
+
+ // Output calculation should not be folded
+ // TODO Reduce code duplication of this kind
+ if (dst_bag->isOutput())
+ {
+ return;
+ }
+
+ // NOTE d->allocated(bag) returns true if bag has corresponding initial
+ // values (e.g. convolution kernel)
+ assert(d->allocated(src_bag));
+ assert(!d->allocated(dst_bag));
+
+ // TODO Support other data type
+ auto src_span = d->f32()->weight(src_bag);
+
+ assert(src_span.data() != nullptr);
+
+ auto src_feature = src_obj->asFeature();
+ auto dst_feature = dst_obj->asFeature();
+
+ // TODO Support other object type
+ if (src_feature == nullptr || dst_feature == nullptr)
+ {
+ return;
+ }
+
+ assert(src_feature != nullptr);
+ assert(dst_feature != nullptr);
+
+ // Allocate weight for destination
+ d->f32()->allocate(dst_bag);
+
+ auto dst_span = d->f32()->weight(dst_bag);
+
+ assert(src_feature->layout()->batch() == dst_feature->layout()->batch());
+ assert(src_feature->layout()->depth() == dst_feature->layout()->depth());
+ assert(src_feature->layout()->height() == dst_feature->layout()->height());
+ assert(src_feature->layout()->width() == dst_feature->layout()->width());
+
+ uint32_t const B = src_feature->layout()->batch();
+ uint32_t const C = src_feature->layout()->depth();
+ uint32_t const H = src_feature->layout()->height();
+ uint32_t const W = src_feature->layout()->width();
+
+ for (uint32_t b = 0; b < B; ++b)
+ {
+ for (uint32_t ch = 0; ch < C; ++ch)
+ {
+ for (uint32_t row = 0; row < H; ++row)
+ {
+ for (uint32_t col = 0; col < W; ++col)
+ {
+ auto src_ind = src_feature->layout()->at(b, ch, row, col);
+ auto dst_ind = dst_feature->layout()->at(b, ch, row, col);
+
+ dst_span[dst_ind.value()] = src_span[src_ind.value()];
+ }
+ }
+ }
+ }
+
+ // Let's detach copy
+ copy->from(nullptr);
+ copy->into(nullptr);
+ copy->detach();
+
+ // Let's visit destination bag!
+ q.push(dst_bag);
+}
+
+template <typename Callable>
+void fold_constant_op(std::queue<coco::Bag *> &q, coco::UnaryOp *op, Callable evaluate)
+{
+ auto m = op->module();
+ auto d = enco::data(m);
+
+ auto ins = op->parent();
+ auto eval = ins->asEval();
+
+ // UnaryOp has only one arg
+ auto src_obj = *(op->uses().begin());
+ auto src_bag = src_obj->bag();
+
+ auto dst_obj = eval->out();
+ auto dst_bag = dst_obj->bag();
+
+ // Output calculation should not be folded
+ // TODO Reduce code duplication of this kind
+ if (dst_bag->isOutput())
+ {
+ return;
+ }
+
+ assert(d->allocated(src_bag));
+ assert(!d->allocated(dst_bag));
+
+ // TODO Support other data type
+ auto src_span = d->f32()->weight(src_bag);
+ assert(src_span.data() != nullptr);
+
+ auto src_feature = src_obj->asFeature();
+ auto dst_feature = dst_obj->asFeature();
+
+ // TODO Support other object type
+ if (src_feature == nullptr || dst_feature == nullptr)
+ {
+ return;
+ }
+
+ assert(src_feature != nullptr);
+ assert(dst_feature != nullptr);
+
+ // Allocate weight for destination
+ d->f32()->allocate(dst_bag);
+ auto dst_span = d->f32()->weight(dst_bag);
+
+ assert(src_feature->layout()->batch() == dst_feature->layout()->batch());
+ assert(src_feature->layout()->depth() == dst_feature->layout()->depth());
+ assert(src_feature->layout()->height() == dst_feature->layout()->height());
+ assert(src_feature->layout()->width() == dst_feature->layout()->width());
+
+ uint32_t const B = src_feature->layout()->batch();
+ uint32_t const C = src_feature->layout()->depth();
+ uint32_t const H = src_feature->layout()->height();
+ uint32_t const W = src_feature->layout()->width();
+
+ for (uint32_t b = 0; b < B; ++b)
+ {
+ for (uint32_t ch = 0; ch < C; ++ch)
+ {
+ for (uint32_t row = 0; row < H; ++row)
+ {
+ for (uint32_t col = 0; col < W; ++col)
+ {
+ auto src_ind = src_feature->layout()->at(b, ch, row, col);
+ auto dst_ind = dst_feature->layout()->at(b, ch, row, col);
+
+ evaluate(&dst_span[dst_ind.value()], src_span[src_ind.value()]);
+ }
+ }
+ }
+ }
+
+ // Let's detach eval
+ eval->out(nullptr);
+ eval->detach();
+
+ // Let's visit destination bag!
+ q.push(dst_bag);
+}
+
+template <typename Callable>
+void fold_constant_op(std::queue<coco::Bag *> &q, coco::BinaryOp *op, Callable evaluate)
+{
+ auto m = op->module();
+ auto d = enco::data(m);
+
+ auto ins = op->parent();
+ auto eval = ins->asEval();
+
+ // Already folded by the other bag
+ if (!eval->out())
+ {
+ return;
+ }
+
+ auto lhs_load = op->left()->asLoad();
+ auto lhs_obj = lhs_load->object();
+ auto lhs_bag = lhs_obj->bag();
+
+ auto rhs_load = op->right()->asLoad();
+ auto rhs_obj = rhs_load->object();
+ auto rhs_bag = rhs_obj->bag();
+
+ auto dst_obj = eval->out();
+ auto dst_bag = dst_obj->bag();
+
+ // Output calculation should not be folded
+ // TODO Reduce code duplication of this kind
+ if (dst_bag->isOutput())
+ {
+ return;
+ }
+
+ // The other bag is non-constant
+ if (!d->allocated(lhs_bag) || !d->allocated(rhs_bag))
+ {
+ return;
+ }
+
+ assert(d->allocated(lhs_bag));
+ assert(d->allocated(rhs_bag));
+ assert(!d->allocated(dst_bag));
+
+ // TODO Support other data type
+ auto lhs_span = d->f32()->weight(lhs_bag);
+ auto rhs_span = d->f32()->weight(rhs_bag);
+ assert(lhs_span.data() != nullptr);
+ assert(rhs_span.data() != nullptr);
+
+ auto lhs_feature = lhs_obj->asFeature();
+ auto rhs_feature = rhs_obj->asFeature();
+ auto dst_feature = dst_obj->asFeature();
+
+ // TODO Support other object type
+ if (lhs_feature == nullptr || rhs_feature == nullptr || dst_feature == nullptr)
+ {
+ return;
+ }
+
+ assert(lhs_feature != nullptr);
+ assert(rhs_feature != nullptr);
+ assert(dst_feature != nullptr);
+
+ // Allocate weight for destination
+ d->f32()->allocate(dst_bag);
+ auto dst_span = d->f32()->weight(dst_bag);
+
+ assert(lhs_feature->layout()->batch() == rhs_feature->layout()->batch());
+ assert(lhs_feature->layout()->depth() == rhs_feature->layout()->depth());
+ assert(lhs_feature->layout()->height() == rhs_feature->layout()->height());
+ assert(lhs_feature->layout()->width() == rhs_feature->layout()->width());
+
+ assert(lhs_feature->layout()->batch() == dst_feature->layout()->batch());
+ assert(lhs_feature->layout()->depth() == dst_feature->layout()->depth());
+ assert(lhs_feature->layout()->height() == dst_feature->layout()->height());
+ assert(lhs_feature->layout()->width() == dst_feature->layout()->width());
+
+ uint32_t const B = lhs_feature->layout()->batch();
+ uint32_t const C = lhs_feature->layout()->depth();
+ uint32_t const H = lhs_feature->layout()->height();
+ uint32_t const W = lhs_feature->layout()->width();
+
+ for (uint32_t b = 0; b < B; ++b)
+ {
+ for (uint32_t ch = 0; ch < C; ++ch)
+ {
+ for (uint32_t row = 0; row < H; ++row)
+ {
+ for (uint32_t col = 0; col < W; ++col)
+ {
+ auto lhs_ind = lhs_feature->layout()->at(b, ch, row, col);
+ auto rhs_ind = rhs_feature->layout()->at(b, ch, row, col);
+ auto dst_ind = dst_feature->layout()->at(b, ch, row, col);
+
+ evaluate(&dst_span[dst_ind.value()], lhs_span[lhs_ind.value()],
+ rhs_span[rhs_ind.value()]);
+ }
+ }
+ }
+ }
+
+ // Let's detach eval
+ eval->out(nullptr);
+ eval->detach();
+
+ // Let's visit destination bag!
+ q.push(dst_bag);
+}
+
+void fold_constant(std::queue<coco::Bag *> &q, coco::Eval *eval)
+{
+ // TODO Support other data types
+ if (auto op = eval->op()->asSqrt())
+ {
+ fold_constant_op(q, op, [](float *dst, float value) { *dst = std::sqrt(value); });
+ }
+ else if (auto op = eval->op()->asAdd())
+ {
+ fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs + rhs; });
+ }
+ else if (auto op = eval->op()->asSub())
+ {
+ fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs - rhs; });
+ }
+ else if (auto op = eval->op()->asMul())
+ {
+ fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs * rhs; });
+ }
+ else if (auto op = eval->op()->asDiv())
+ {
+ fold_constant_op(q, op, [](float *dst, float lhs, float rhs) { *dst = lhs / rhs; });
+ }
+ else
+ {
+ // Not supported opteration, do nothing
+ // TODO Support other operations
+ }
+}
+
+void fold_constant(std::queue<coco::Bag *> &q, coco::Instr *ins)
+{
+ if (auto copy = coco::safe_cast<coco::Copy>(ins))
+ {
+ fold_constant(q, copy);
+ return;
+ }
+ if (auto eval = coco::safe_cast<coco::Eval>(ins))
+ {
+ fold_constant(q, eval);
+ return;
+ }
+
+ // TODO Add more cases for constant folding
+}
+
+} // namespace
+
+namespace enco
+{
+
+void fold_constants(enco::Code *code)
+{
+ std::queue<coco::Bag *> q;
+
+ // Collect the initial set of "constant" bag
+ constant_bag_enumerator(code) << [&q](coco::Bag *bag) { q.push(bag); };
+
+ while (!q.empty())
+ {
+ auto candidate_bag = take(q);
+
+ // Scan the readers of each candidate bag
+ for (auto reader : coco::readers(candidate_bag))
+ {
+ // TODO Decide how to handle the reader with unknown instruction
+ if (auto ins = reader->loc())
+ {
+ fold_constant(q, ins);
+ }
+ }
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.h b/compiler/enco/core/src/Transforms/ConstantFolding.h
new file mode 100644
index 000000000..6faa9c876
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/ConstantFolding.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __CONSTANT_FOLDING_H__
+#define __CONSTANT_FOLDING_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Evaluate "constant" expressions at compile time
+ */
+void fold_constants(enco::Code *);
+
+struct ConstantFoldingPass final : public Pass
+{
+ PASS_CTOR(ConstantFoldingPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { fold_constants(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __CONSTANT_FOLDING_H__
diff --git a/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp b/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp
new file mode 100644
index 000000000..5ac71ac14
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/ConstantFolding.test.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2019 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 "ConstantFolding.h"
+#include "Session.h"
+
+#include <cmath>
+#include <gtest/gtest.h>
+
+namespace
+{
+
+class BinaryNetwork
+{
+public:
+ BinaryNetwork(coco::Module *module, coco::Data *data) : _module{module}, _data{data}
+ {
+ // DO NOTHING
+ }
+
+ template <typename Op> void build(void);
+
+ void fold(void)
+ {
+ // Execute constant folding
+ enco::make_session(_module, _data);
+ enco::Code code{_module, _data};
+ enco::fold_constants(&code);
+ }
+
+public:
+ coco::Bag *out;
+ coco::Bag *lhs;
+ coco::Bag *rhs;
+
+ coco::Eval *eval;
+
+private:
+ coco::Module *_module;
+ coco::Data *_data;
+};
+
+template <typename Op> void BinaryNetwork::build(void)
+{
+ // Create lhs bag and object
+ auto lhs_bag = _module->entity()->bag()->create(12);
+ auto lhs_obj = _module->entity()->object()->template create<coco::FeatureObject>();
+ coco::FeatureShape lhs_shape(1, 2, 2, 3);
+ lhs_obj->bag(lhs_bag);
+ lhs_obj->layout(coco::FeatureLayouts::BHWC::create(lhs_shape));
+
+ // Create rhs bag and object
+ auto rhs_bag = _module->entity()->bag()->create(12);
+ auto rhs_obj = _module->entity()->object()->template create<coco::FeatureObject>();
+ coco::FeatureShape rhs_shape(1, 2, 2, 3);
+ rhs_obj->bag(rhs_bag);
+ rhs_obj->layout(coco::FeatureLayouts::BHWC::create(rhs_shape));
+
+ // Create output bag and object
+ auto output_bag = _module->entity()->bag()->create(12);
+ auto output_obj = _module->entity()->object()->template create<coco::FeatureObject>();
+ coco::FeatureShape ofm_shape(1, 2, 2, 3);
+ output_obj->bag(output_bag);
+ output_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape));
+
+ // Create instruction and operations
+ auto block = _module->entity()->block()->create();
+ auto eval = _module->entity()->instr()->template create<coco::Eval>();
+ auto load_lhs = _module->entity()->op()->template create<coco::Load>();
+ auto load_rhs = _module->entity()->op()->template create<coco::Load>();
+ auto add_op = _module->entity()->op()->template create<Op>();
+
+ _module->block()->append(block);
+ block->instr()->append(eval);
+
+ load_lhs->object(lhs_obj);
+ load_rhs->object(rhs_obj);
+ add_op->left(load_lhs);
+ add_op->right(load_rhs);
+
+ eval->op(add_op);
+ eval->out(output_obj);
+
+ // Create a handle
+ this->lhs = lhs_bag;
+ this->rhs = rhs_bag;
+ this->out = output_bag;
+
+ this->eval = eval;
+}
+
+} // namespace
+
+TEST(ConstantFoldingTest, sqrt)
+{
+ auto module = coco::Module::create();
+ auto data = coco::Data::create();
+
+ // Create input bag and object
+ auto input_bag = module->entity()->bag()->create(12);
+ auto input_obj = module->entity()->object()->create<coco::FeatureObject>();
+ coco::FeatureShape ifm_shape(1, 2, 2, 3);
+ input_obj->bag(input_bag);
+ input_obj->layout(coco::FeatureLayouts::BHWC::create(ifm_shape));
+
+ // Create output bag and object
+ auto output_bag = module->entity()->bag()->create(12);
+ auto output_obj = module->entity()->object()->create<coco::FeatureObject>();
+ coco::FeatureShape ofm_shape(1, 2, 2, 3);
+ output_obj->bag(output_bag);
+ output_obj->layout(coco::FeatureLayouts::BHWC::create(ofm_shape));
+
+ // Insert values into input bag
+ data->f32()->allocate(input_bag);
+ auto input = data->f32()->weight(input_bag);
+ for (uint32_t idx = 0; idx < input.size(); ++idx)
+ {
+ input[idx] = (float)idx;
+ }
+
+ // Create instruction and operations
+ auto block = module->entity()->block()->create();
+ auto eval = module->entity()->instr()->create<coco::Eval>();
+ auto load = module->entity()->op()->create<coco::Load>();
+ auto sqrt_op = module->entity()->op()->create<coco::Sqrt>();
+
+ module->block()->append(block);
+ block->instr()->append(eval);
+
+ load->object(input_obj);
+ sqrt_op->arg(load);
+
+ eval->op(sqrt_op);
+ eval->out(output_obj);
+
+ // Execute constant folding
+ enco::make_session(module.get(), data.get());
+ enco::Code code{module.get(), data.get()};
+ enco::fold_constants(&code);
+
+ // Validate the result
+ ASSERT_EQ(data->allocated(output_bag), true);
+ ASSERT_EQ(eval->out(), nullptr);
+
+ auto output = data->f32()->weight(output_bag);
+ for (uint32_t idx = 0; idx < output.size(); ++idx)
+ {
+ ASSERT_FLOAT_EQ(output[idx], std::sqrt(input[idx]));
+ }
+}
+
+TEST(ConstantFoldingTest, element_wise_add)
+{
+ auto module = coco::Module::create();
+ auto data = coco::Data::create();
+
+ BinaryNetwork net{module.get(), data.get()};
+
+ // Build a network
+ net.build<coco::Add>();
+
+ // Create alises
+ auto lhs_bag = net.lhs;
+ auto rhs_bag = net.rhs;
+ auto output_bag = net.out;
+ auto eval = net.eval;
+
+ // Insert values into lhs and rhs bag
+ data->f32()->allocate(lhs_bag);
+ data->f32()->allocate(rhs_bag);
+ auto lhs = data->f32()->weight(lhs_bag);
+ auto rhs = data->f32()->weight(rhs_bag);
+ for (uint32_t idx = 0; idx < lhs.size(); ++idx)
+ {
+ lhs[idx] = (float)idx;
+ rhs[idx] = 1.5;
+ }
+
+ // Execute constant folding
+ net.fold();
+
+ // Validate the result
+ ASSERT_EQ(data->allocated(output_bag), true);
+ ASSERT_EQ(eval->out(), nullptr);
+
+ auto output = data->f32()->weight(output_bag);
+ for (uint32_t idx = 0; idx < output.size(); ++idx)
+ {
+ ASSERT_FLOAT_EQ(output[idx], lhs[idx] + rhs[idx]);
+ }
+}
+
+TEST(ConstantFoldingTest, element_wise_sub)
+{
+ auto module = coco::Module::create();
+ auto data = coco::Data::create();
+
+ BinaryNetwork net{module.get(), data.get()};
+
+ // Build a network
+ net.build<coco::Sub>();
+
+ // Create alises
+ auto lhs_bag = net.lhs;
+ auto rhs_bag = net.rhs;
+ auto output_bag = net.out;
+ auto eval = net.eval;
+
+ // Insert values into lhs and rhs bag
+ data->f32()->allocate(lhs_bag);
+ data->f32()->allocate(rhs_bag);
+ auto lhs = data->f32()->weight(lhs_bag);
+ auto rhs = data->f32()->weight(rhs_bag);
+ for (uint32_t idx = 0; idx < lhs.size(); ++idx)
+ {
+ lhs[idx] = (float)idx;
+ rhs[idx] = 1.5;
+ }
+
+ // Execute constant folding
+ net.fold();
+
+ // Validate the result
+ ASSERT_EQ(data->allocated(output_bag), true);
+ ASSERT_EQ(eval->out(), nullptr);
+
+ auto output = data->f32()->weight(output_bag);
+ for (uint32_t idx = 0; idx < output.size(); ++idx)
+ {
+ ASSERT_FLOAT_EQ(output[idx], lhs[idx] - rhs[idx]);
+ }
+}
+
+TEST(ConstantFoldingTest, element_wise_mul)
+{
+ auto module = coco::Module::create();
+ auto data = coco::Data::create();
+
+ BinaryNetwork net{module.get(), data.get()};
+
+ // Build a network
+ net.build<coco::Mul>();
+
+ // Create alises
+ auto lhs_bag = net.lhs;
+ auto rhs_bag = net.rhs;
+ auto output_bag = net.out;
+ auto eval = net.eval;
+
+ // Insert values into lhs and rhs bag
+ data->f32()->allocate(lhs_bag);
+ data->f32()->allocate(rhs_bag);
+ auto lhs = data->f32()->weight(lhs_bag);
+ auto rhs = data->f32()->weight(rhs_bag);
+ for (uint32_t idx = 0; idx < lhs.size(); ++idx)
+ {
+ lhs[idx] = (float)idx;
+ rhs[idx] = 1.5;
+ }
+
+ // Execute constant folding
+ net.fold();
+
+ // Validate the result
+ ASSERT_EQ(data->allocated(output_bag), true);
+ ASSERT_EQ(eval->out(), nullptr);
+
+ auto output = data->f32()->weight(output_bag);
+ for (uint32_t idx = 0; idx < output.size(); ++idx)
+ {
+ ASSERT_FLOAT_EQ(output[idx], lhs[idx] * rhs[idx]);
+ }
+}
+
+TEST(ConstantFoldingTest, element_wise_div)
+{
+ auto module = coco::Module::create();
+ auto data = coco::Data::create();
+
+ BinaryNetwork net{module.get(), data.get()};
+
+ // Build a network
+ net.build<coco::Div>();
+
+ // Create alises
+ auto lhs_bag = net.lhs;
+ auto rhs_bag = net.rhs;
+ auto output_bag = net.out;
+ auto eval = net.eval;
+
+ // Insert values into lhs and rhs bag
+ data->f32()->allocate(lhs_bag);
+ data->f32()->allocate(rhs_bag);
+ auto lhs = data->f32()->weight(lhs_bag);
+ auto rhs = data->f32()->weight(rhs_bag);
+ for (uint32_t idx = 0; idx < lhs.size(); ++idx)
+ {
+ lhs[idx] = (float)idx;
+ rhs[idx] = 1.5;
+ }
+
+ // Execute constant folding
+ net.fold();
+
+ // Validate the result
+ ASSERT_EQ(data->allocated(output_bag), true);
+ ASSERT_EQ(eval->out(), nullptr);
+
+ auto output = data->f32()->weight(output_bag);
+ for (uint32_t idx = 0; idx < output.size(); ++idx)
+ {
+ ASSERT_FLOAT_EQ(output[idx], lhs[idx] / rhs[idx]);
+ }
+}
diff --git a/compiler/enco/core/src/Transforms/CopyLowering.cpp b/compiler/enco/core/src/Transforms/CopyLowering.cpp
new file mode 100644
index 000000000..ceb3bbd5c
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/CopyLowering.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "CopyLowering.h"
+
+#include <set>
+#include <cassert>
+
+//
+// Lower Copy as Shuffle
+//
+namespace enco
+{
+
+void lower_copy(enco::Code *code)
+{
+ auto m = code->module();
+
+ std::set<coco::Copy *> lowered_copies;
+
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ auto ins = m->entity()->instr()->at(n);
+
+ assert(ins != nullptr);
+
+ if (ins->parent() == nullptr)
+ {
+ // Skip if instruction does not belong to a list
+ continue;
+ }
+
+ auto copy = ins->asCopy();
+
+ if (copy == nullptr)
+ {
+ // Skip if instruction is not a copy
+ continue;
+ }
+
+ // TODO Support non-Feature objects
+ auto ifm = copy->from()->asFeature();
+ auto ofm = copy->into()->asFeature();
+
+ if ((ifm == nullptr) || (ofm == nullptr))
+ {
+ continue;
+ }
+
+ assert(ifm->layout()->batch() == ofm->layout()->batch());
+ assert(ifm->layout()->shape() == ofm->layout()->shape());
+
+ auto shuffle = m->entity()->instr()->create<coco::Shuffle>();
+
+ shuffle->from(ifm->bag());
+ shuffle->into(ofm->bag());
+
+ const uint32_t B = ifm->layout()->batch();
+ const uint32_t C = ifm->layout()->shape().depth();
+ const uint32_t H = ifm->layout()->shape().height();
+ const uint32_t W = ifm->layout()->shape().width();
+
+ for (uint32_t b = 0; b < B; ++b)
+ {
+ for (uint32_t ch = 0; ch < C; ++ch)
+ {
+ for (uint32_t row = 0; row < H; ++row)
+ {
+ for (uint32_t col = 0; col < W; ++col)
+ {
+ const auto from = ifm->layout()->at(b, ch, row, col);
+ const auto into = ofm->layout()->at(b, ch, row, col);
+
+ shuffle->insert(from, into);
+ }
+ }
+ }
+ }
+
+ shuffle->insertBefore(copy);
+ lowered_copies.insert(copy);
+ }
+
+ // Destroy lowered copy
+ for (const auto &copy : lowered_copies)
+ {
+ copy->detach();
+ m->entity()->instr()->destroy(copy);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/CopyLowering.h b/compiler/enco/core/src/Transforms/CopyLowering.h
new file mode 100644
index 000000000..51f0f83e2
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/CopyLowering.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __ENCO_LOWER_H__
+#define __ENCO_LOWER_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Lower copy(...) instruction into shuffle(...)
+ */
+void lower_copy(enco::Code *code);
+
+struct CopyLoweringPass final : public Pass
+{
+ PASS_CTOR(CopyLoweringPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { lower_copy(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_LOWER_H__
diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp b/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp
new file mode 100644
index 000000000..9d65d1c0b
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.cpp
@@ -0,0 +1,383 @@
+/*
+ * 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 "DataLayoutConversion.h"
+#include "Session.h"
+#include "IRUtils.h"
+
+#include "coex/IR.h"
+
+#include <coco/IR/FeatureLayouts.h>
+#include <coco/IR/KernelLayouts.h>
+
+#include <nncc/core/ADT/feature/Layout.h>
+#include <nncc/core/ADT/kernel/Layout.h>
+
+#include <nncc/core/ADT/feature/HWCLayout.h>
+#include <nncc/core/ADT/kernel/NHWCLayout.h>
+
+#include <set>
+
+namespace
+{
+
+coco::Copy *make_copy(coco::FeatureObject *from, coco::FeatureObject *into)
+{
+ auto m = from->module();
+ assert(m != nullptr);
+ assert(from->module() == into->module());
+
+ auto copy = m->entity()->instr()->create<coco::Copy>();
+
+ copy->from(from);
+ copy->into(into);
+
+ return copy;
+}
+
+coco::FeatureObject *clone_feature(const coco::FeatureObject *oldobj)
+{
+ auto module = oldobj->module();
+ auto newobj = module->entity()->object()->create<coco::FeatureObject>();
+ newobj->layout(coco::FeatureLayouts::BHWC::create(oldobj->shape()));
+
+ if (oldobj->bag() != nullptr)
+ {
+ using nncc::core::ADT::feature::num_elements;
+
+ // NOTE The size of bag should be at least "BxHxWxC" as "newobj" uses BHWC layout
+ const uint32_t batch = newobj->layout()->batch();
+ const uint32_t count = num_elements(newobj->layout()->shape());
+ const uint32_t bag_size = batch * count;
+
+ // Clone bag only when there is a backing bag for a given feature object
+ auto newbag = module->entity()->bag()->create(bag_size);
+ newobj->bag(newbag);
+ }
+
+ return newobj;
+}
+
+/**
+ * @brief Insert Copy before Load if necessary
+ *
+ * @require "load" should be bounded
+ */
+void insert_copy_before_load(coco::Load *load)
+{
+ assert(load->parent() != nullptr);
+ assert(load->parent()->parent() != nullptr);
+
+ if (auto obj = load->object())
+ {
+ if (auto ifm = obj->asFeature())
+ {
+ if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ auto oldobj = ifm;
+ auto newobj = clone_feature(oldobj);
+
+ load->object(newobj);
+
+ auto copy = make_copy(oldobj, newobj);
+ copy->insertBefore(load->parent());
+ }
+ }
+ }
+}
+
+/**
+ * @brief Insert Copy after Eval if necessary
+ */
+void insert_copy_after_eval(coco::Eval *eval)
+{
+ if (auto out = eval->out())
+ {
+ if (auto ofm = out->asFeature())
+ {
+ if (ofm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ auto oldobj = ofm;
+ auto newobj = clone_feature(oldobj);
+
+ eval->out(newobj);
+
+ auto copy = make_copy(newobj, oldobj);
+ copy->insertAfter(eval);
+ }
+ }
+ }
+}
+
+/**
+ * @brief Insert copy (for data layout change) before/after ANNDepthConcatF if necessary
+ */
+void convert_data_layout(ANNDepthConcatF *concat)
+{
+ if (auto out = concat->out())
+ {
+ if (auto ofm = out->asFeature())
+ {
+ if (ofm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ auto oldobj = ofm;
+ auto newobj = clone_feature(oldobj);
+
+ concat->out(newobj);
+
+ auto copy = make_copy(newobj, oldobj);
+ copy->insertAfter(concat);
+ }
+ }
+ }
+
+ if (auto obj = concat->fst())
+ {
+ if (auto ifm = obj->asFeature())
+ {
+ if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ auto oldobj = ifm;
+ auto newobj = clone_feature(oldobj);
+
+ concat->fst(newobj);
+
+ auto copy = make_copy(oldobj, newobj);
+ copy->insertBefore(concat);
+ }
+ }
+ }
+
+ if (auto obj = concat->snd())
+ {
+ if (auto ifm = obj->asFeature())
+ {
+ if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ auto oldobj = ifm;
+ auto newobj = clone_feature(oldobj);
+
+ concat->snd(newobj);
+
+ auto copy = make_copy(oldobj, newobj);
+ copy->insertBefore(concat);
+ }
+ }
+ }
+}
+
+/**
+ * @brief Update convolution kernel data layout
+ */
+void change_conv2d_kernel_layout(coco::Conv2D *conv)
+{
+ auto m = conv->module();
+ assert(m != nullptr);
+ auto d = enco::data(enco::session(m));
+ assert(d != nullptr);
+
+ auto old_obj = conv->ker();
+ assert(old_obj != nullptr);
+ auto old_bag = old_obj->bag();
+ assert(old_bag != nullptr);
+
+ if (old_obj->layout()->id() == coco::KernelLayouts::NHWC::uid())
+ {
+ // Skip if kernel already uses NHWC layout
+ return;
+ }
+
+ const auto &ker_shape = old_obj->shape();
+
+ assert(d->allocated(old_bag));
+
+ auto new_bag = m->entity()->bag()->create(old_bag->size());
+ auto new_obj = m->entity()->object()->create<coco::KernelObject>();
+
+ new_obj->bag(new_bag);
+ new_obj->layout(coco::KernelLayouts::NHWC::create(ker_shape));
+
+ d->f32()->allocate(new_bag);
+
+ auto src = d->f32()->read(old_obj);
+ auto dst = d->f32()->access(new_obj);
+
+ const auto ker_N = ker_shape.count();
+ const auto ker_C = ker_shape.depth();
+ const auto ker_H = ker_shape.height();
+ const auto ker_W = ker_shape.width();
+
+ for (uint32_t n = 0; n < ker_N; ++n)
+ {
+ for (uint32_t ch = 0; ch < ker_C; ++ch)
+ {
+ for (uint32_t row = 0; row < ker_H; ++row)
+ {
+ for (uint32_t col = 0; col < ker_W; ++col)
+ {
+ dst->at(n, ch, row, col) = src->at(n, ch, row, col);
+ }
+ }
+ }
+ }
+
+ conv->ker(new_obj);
+ d->release(old_bag);
+}
+
+} // namespace
+
+namespace
+{
+
+/**
+ * @brief Return the set of all of bounded Load Op(s) in a given module
+ *
+ * @note 'bounded' means it will be exectuted
+ */
+std::set<coco::Load *> loads(coco::Module *m)
+{
+ std::set<coco::Load *> res;
+
+ for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
+ {
+ auto op = m->entity()->op()->at(n);
+
+ // Skip if this op is dangling
+ if (op->parent() == nullptr)
+ {
+ continue;
+ }
+
+ // Skip if eval instruction of this op is dangling
+ if (op->parent()->parent() == nullptr)
+ {
+ continue;
+ }
+
+ if (auto load = m->entity()->op()->at(n)->asLoad())
+ {
+ res.insert(load);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * @brief Return the set of every (allocated) Eval instruction in a given module
+ */
+std::set<coco::Eval *> evals(coco::Module *m)
+{
+ std::set<coco::Eval *> res;
+
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ if (auto eval = m->entity()->instr()->at(n)->asEval())
+ {
+ res.insert(eval);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * @brief Return the set of allocated Conv2D op in a given module
+ */
+std::set<coco::Conv2D *> convs(coco::Module *m)
+{
+ std::set<coco::Conv2D *> res;
+
+ for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
+ {
+ if (auto op = m->entity()->op()->at(n)->asConv2D())
+ {
+ res.insert(op);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * @brief Return the set of "bounded" ANNDepthConcatF instructions
+ */
+std::set<ANNDepthConcatF *> depth_concats(coco::Module *m)
+{
+ std::set<ANNDepthConcatF *> res;
+
+ for (auto ins : enco::instr_sequence(m))
+ {
+ if (auto depth_concat_f = coco::safe_cast<ANNDepthConcatF>(ins))
+ {
+ res.insert(depth_concat_f);
+ }
+ }
+
+ return res;
+}
+
+class NormalizePass
+{
+private:
+ void runOnModule(coco::Module *m) const;
+
+public:
+ void runOnCode(enco::Code *) const;
+};
+
+void NormalizePass::runOnModule(coco::Module *m) const
+{
+ // Insert Copy before all Load Op (if necessary)
+ for (auto load : loads(m))
+ {
+ insert_copy_before_load(load);
+ }
+
+ // Insert Copy after all Eval Instr (if necessary)
+ for (auto eval : evals(m))
+ {
+ insert_copy_after_eval(eval);
+ }
+
+ // Change Kernel Layout of Conv2D opertion (if necessary)
+ for (auto conv : convs(m))
+ {
+ change_conv2d_kernel_layout(conv);
+ }
+
+ // Insert Copy (for Layout Conversion) before/after ANNDepthConcatF instructions (if necessary)
+ for (auto depth_concat : depth_concats(m))
+ {
+ convert_data_layout(depth_concat);
+ }
+}
+
+void NormalizePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
+
+} // namespace
+
+namespace enco
+{
+
+void convert_data_layout(enco::Code *code)
+{
+ NormalizePass pass;
+ pass.runOnCode(code);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.h b/compiler/enco/core/src/Transforms/DataLayoutConversion.h
new file mode 100644
index 000000000..ac4052c8b
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__
+#define __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Insert data reordering if necessary
+ */
+void convert_data_layout(enco::Code *code);
+
+struct DataLayoutConversionPass final : public enco::Pass
+{
+ PASS_CTOR(DataLayoutConversionPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { convert_data_layout(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_DATA_LAYOUT_CONVERSION_H__
diff --git a/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp b/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp
new file mode 100644
index 000000000..812e38a78
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DataLayoutConversion.test.cpp
@@ -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 "DataLayoutConversion.h"
+
+#include <gtest/gtest.h>
+
+TEST(DataLayoutConversionTest, case_000)
+{
+ auto m = coco::Module::create();
+
+ // Create a "free" Load op
+ m->entity()->instr()->create<coco::Eval>();
+
+ enco::Code code{m.get(), nullptr};
+ ASSERT_EQ(m->entity()->instr()->size(), 1);
+
+ // "conver_data_layout" SHOULD NOT crash even if there is a "free" Load op
+ enco::convert_data_layout(&code);
+}
diff --git a/compiler/enco/core/src/Transforms/DeadBagElimination.cpp b/compiler/enco/core/src/Transforms/DeadBagElimination.cpp
new file mode 100644
index 000000000..b3c598a55
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DeadBagElimination.cpp
@@ -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.
+ */
+
+#include "DeadBagElimination.h"
+
+#include <set>
+
+namespace
+{
+
+/// @brief Return true if a given bag is marked as either input or output
+bool is_public(const coco::Bag *b) { return b->isInput() || b->isOutput(); }
+
+/// @brief Return the set of "dead" bags in a given module
+std::set<coco::Bag *> dead_bags(const coco::Module *m)
+{
+ std::set<coco::Bag *> res;
+
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ if (coco::readers(bag).empty() && !is_public(bag))
+ {
+ res.insert(bag);
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void eliminate_dead_bag(enco::Code *code)
+{
+ auto m = code->module();
+
+ // Destroy a dead bag and its updaters
+ for (auto bag : dead_bags(m))
+ {
+ for (auto updater : coco::updaters(bag))
+ {
+ auto ins = updater->loc();
+
+ assert(ins != nullptr);
+
+ ins->detach();
+ m->entity()->instr()->destroy(ins);
+ }
+
+ bag->replaceWith(nullptr);
+ m->entity()->bag()->destroy(bag);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/DeadBagElimination.h b/compiler/enco/core/src/Transforms/DeadBagElimination.h
new file mode 100644
index 000000000..87e03e8ac
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DeadBagElimination.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__
+#define __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Eliminate dead bags
+ *
+ * A bag is referred to as dead if it is neither input nor output, and has no read. If a bag is
+ * dead, it is unnecessary to updates its values as these values are never used.
+ *
+ * "eliminate_dead_bag" removes all the dead bags and its updaters from IR.
+ */
+void eliminate_dead_bag(enco::Code *code);
+
+struct DeadBagEliminationPass final : public Pass
+{
+ PASS_CTOR(DeadBagEliminationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { eliminate_dead_bag(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_DEAD_BAG_ELIMINATION_H__
diff --git a/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp b/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp
new file mode 100644
index 000000000..df8cc628a
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DeadObjectElimination.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "DeadObjectElimination.h"
+
+#include <set>
+
+namespace
+{
+
+std::set<coco::Object *> dead_objects(const coco::Module *m)
+{
+ std::set<coco::Object *> res;
+
+ for (uint32_t n = 0; n < m->entity()->object()->size(); ++n)
+ {
+ auto obj = m->entity()->object()->at(n);
+
+ if (auto bag = obj->bag())
+ {
+ if (coco::readers(bag).empty() && !(bag->isOutput()))
+ {
+ res.insert(obj);
+ }
+ }
+ else
+ {
+ // NOTE Just in case if there are Objects not related to Bags
+ if (obj->uses()->size() == 0)
+ {
+ res.insert(obj);
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void eliminate_dead_object(enco::Code *code)
+{
+ auto m = code->module();
+
+ // Destroy a dead object and its producer
+ for (auto obj : dead_objects(m))
+ {
+ if (auto producer = coco::producer(obj))
+ {
+ auto ins = producer->loc();
+ assert(ins != nullptr);
+
+ ins->detach();
+ m->entity()->instr()->destroy(ins);
+ }
+
+ m->entity()->object()->destroy(obj);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/DeadObjectElimination.h b/compiler/enco/core/src/Transforms/DeadObjectElimination.h
new file mode 100644
index 000000000..4923e56fd
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DeadObjectElimination.h
@@ -0,0 +1,47 @@
+/*
+ * 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 __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__
+#define __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Eliminate dead objects in IR
+ *
+ * An object whose backing bag is unused is referred to as a dead object.
+ *
+ * Dead Object Elimination (DOE) eliminates such dead objects along with their producer.
+ */
+void eliminate_dead_object(enco::Code *code);
+
+struct DeadObjectEliminationPass final : public Pass
+{
+ PASS_CTOR(DeadObjectEliminationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { eliminate_dead_object(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_DEAD_OBJECT_ELIMINATION_H__
diff --git a/compiler/enco/core/src/Transforms/Duplicate.cpp b/compiler/enco/core/src/Transforms/Duplicate.cpp
new file mode 100644
index 000000000..91f64a0ad
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Duplicate.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 "Duplicate.h"
+
+#include <map>
+#include <set>
+
+#include <cassert>
+
+namespace
+{
+
+coco::Block *find_or_create_first_block(coco::Module *m)
+{
+ if (m->block()->empty())
+ {
+ auto blk = m->entity()->block()->create();
+ m->block()->append(blk);
+ return blk;
+ }
+
+ return m->block()->head();
+}
+
+} // namespace
+
+namespace
+{
+
+class DuplicatePass
+{
+private:
+ void runOnModule(coco::Module *m) const;
+
+public:
+ void runOnCode(enco::Code *) const;
+};
+
+void DuplicatePass::runOnModule(coco::Module *m) const
+{
+ // Let's find candidates
+ std::set<coco::Bag *> candidates;
+
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ if (bag->isInput() && bag->isOutput())
+ {
+ candidates.insert(bag);
+ }
+ }
+
+ // Return if there is no candidate
+ if (candidates.empty())
+ {
+ return;
+ }
+
+ std::map<const coco::Bag *, coco::Input *> input_map;
+ std::map<const coco::Bag *, coco::Output *> output_map;
+
+ for (uint32_t n = 0; n < m->input()->size(); ++n)
+ {
+ auto input = m->input()->at(n);
+ assert(input->bag() != nullptr);
+ input_map[input->bag()] = input;
+ }
+
+ for (uint32_t n = 0; n < m->output()->size(); ++n)
+ {
+ auto output = m->output()->at(n);
+ assert(output->bag() != nullptr);
+ output_map[output->bag()] = output;
+ }
+
+ // For each in/out bag,
+ // 1. Create a new bag of the same size
+ // 2. Copy the content from the original bag
+ // 3. Mark the newly created bag as an output
+ for (const auto &candidate : candidates)
+ {
+ assert(coco::updaters(candidate).empty());
+ assert(input_map.find(candidate) != input_map.end());
+ assert(output_map.find(candidate) != output_map.end());
+
+ auto src = candidate;
+ auto dst = m->entity()->bag()->create(src->size());
+
+ // Create a copy instruction
+ auto shuffle = m->entity()->instr()->create<coco::Shuffle>();
+
+ shuffle->from(src);
+ shuffle->into(dst);
+
+ for (uint32_t n = 0; n < src->size(); ++n)
+ {
+ shuffle->insert(coco::ElemID{n} /* FROM */, coco::ElemID{n} /* INTO */);
+ }
+
+ find_or_create_first_block(m)->instr()->prepend(shuffle);
+
+ // Let's use the new bag as an output
+ output_map.at(src)->bag(dst);
+ }
+}
+
+void DuplicatePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
+
+} // namespace
+
+namespace enco
+{
+
+void duplicate_inout_bag(enco::Code *code)
+{
+ DuplicatePass duplicate;
+ duplicate.runOnCode(code);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/Duplicate.h b/compiler/enco/core/src/Transforms/Duplicate.h
new file mode 100644
index 000000000..93baa4589
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Duplicate.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __DUPLICATE_H__
+#define __DUPLICATE_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Eliminate in/out bags by duplication
+ */
+void duplicate_inout_bag(enco::Code *code);
+
+struct BagDuplicationPass final : public Pass
+{
+ PASS_CTOR(BagDuplicationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { duplicate_inout_bag(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __DUPLICATE_H__
diff --git a/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp
new file mode 100644
index 000000000..fa84c005c
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "DuplicatedObjectReduction.h"
+
+#include "CodeIndex.h"
+#include "IRUtils.h"
+
+#include <set>
+
+namespace
+{
+
+/**
+ * @brief Collect feature objects in coco IR
+ */
+std::set<coco::FeatureObject *> features(const coco::Module *m)
+{
+ std::set<coco::FeatureObject *> res;
+
+ for (uint32_t n = 0; n < m->entity()->object()->size(); ++n)
+ {
+ if (auto feature = m->entity()->object()->at(n)->asFeature())
+ {
+ res.insert(feature);
+ }
+ }
+
+ return res;
+}
+
+std::set<coco::FeatureObject *> candidates(const coco::FeatureObject *src)
+{
+ std::set<coco::FeatureObject *> res;
+
+ for (auto consumer : coco::consumers(src))
+ {
+ if (auto copy = consumer->loc()->asCopy())
+ {
+ auto dst = copy->into()->asFeature();
+ assert(dst != nullptr);
+
+ if (dst->layout()->id() == coco::FeatureLayouts::BHWC::uid())
+ {
+ res.insert(dst);
+ }
+ }
+ }
+
+ return res;
+}
+
+CodeIndex code_index(coco::Object::Producer *p)
+{
+ if (auto ins = p->loc())
+ {
+ return ::code_index(ins);
+ }
+
+ return CodeIndex{};
+}
+
+} // namespace
+
+namespace enco
+{
+
+void reduce_duplicated_object(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (const auto &src : features(m))
+ {
+ auto copied = candidates(src);
+
+ if (copied.size() <= 1)
+ {
+ continue;
+ }
+
+ // Find the dominator
+ coco::FeatureObject *dominator = nullptr;
+
+ for (auto candidate : copied)
+ {
+ if (dominator == nullptr)
+ {
+ dominator = candidate;
+ }
+ else if (code_index(coco::producer(candidate)) < code_index(coco::producer(dominator)))
+ {
+ dominator = candidate;
+ }
+ }
+
+ // Replace all the occurunce of dominated objects with its dominator
+ copied.erase(dominator);
+
+ for (auto dominatee : copied)
+ {
+ subst(dominatee, dominator);
+ }
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.h
new file mode 100644
index 000000000..3aa20058e
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/DuplicatedObjectReduction.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 __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__
+#define __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Reduce duplicated feature objects as its dominating feature object
+ *
+ * >>> BEFORE <<<
+ * %obj_0 = Feature(layout: ???) at ...
+ * %obj_1 = Feature(layout: BHWC) at ...
+ * %obj_2 = Feature(layout: BHWC) at ...
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * copy(from: %obj_0, into: %obj_2)
+ *
+ * ...
+ * Use(%obj_1)
+ * Use(%obj_2)
+ * ...
+ *
+ * >>> AFTER <<<
+ * %obj_0 = Feature(layout: ???) at ...
+ * %obj_1 = Feature(layout: BHWC) at ...
+ * %obj_2 = Feature(layout: BHWC) at ...
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * copy(from: %obj_0, into: %obj_2)
+ *
+ * ...
+ * Use(%obj_1)
+ * Use(%obj_1) <-- CHANGED
+ * ...
+ *
+ * NOTE Given a set of feature objects, a feature object referred to as a dominating
+ * feature object if its producer proceeds the producer of every feature object
+ * in the given set
+ */
+void reduce_duplicated_object(enco::Code *code);
+
+struct DuplicatedObjectReductionPass final : public Pass
+{
+ PASS_CTOR(DuplicatedObjectReductionPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { reduce_duplicated_object(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_DUPLICATED_OBJECT_REDUCTION_H__
diff --git a/compiler/enco/core/src/Transforms/FeatureUnification.cpp b/compiler/enco/core/src/Transforms/FeatureUnification.cpp
new file mode 100644
index 000000000..1a7a0a8a4
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FeatureUnification.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 "FeatureUnification.h"
+#include "IRUtils.h"
+
+#include <stdex/Memory.h>
+
+#include <set>
+#include <vector>
+
+#include <cassert>
+
+using stdex::make_unique;
+
+namespace
+{
+
+bool is_static_layout(const coco::FeatureLayout::ID *id)
+{
+ if (id == coco::FeatureLayouts::BHWC::uid())
+ {
+ return true;
+ }
+
+ if (id == coco::FeatureLayouts::BCHW::uid())
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool is_static_layout(const coco::FeatureLayout *l) { return is_static_layout(l->id()); }
+bool is_static_layout(const coco::FeatureObject *f) { return is_static_layout(f->layout()); }
+
+/**
+ * @brief Return ture if a given 'feature' is the candidate of unification
+ */
+bool candidate(const coco::FeatureObject *f) { return is_static_layout(f); }
+
+/**
+ * @brief Return true if two features are compatible
+ *
+ * Two features are referred to as compatible if these feature are interchangeable.
+ *
+ * NOTE The current implementation of "compatible" is sound, but incomplete.
+ *
+ * Soundness:
+ * For all feature objects "lhs" and "rhs" that "compatible(lhs, rhs)" returns true,
+ * "lhs" and "rhs" are interchangeable.
+ *
+ * Completeness:
+ * For all interchangeable feature objects "lhs" and "rhs", "compatible(lhs, rhs)" returns true.
+ */
+bool compatible(const coco::FeatureObject *lhs, const coco::FeatureObject *rhs)
+{
+ assert(candidate(lhs) && candidate(rhs));
+
+ if (lhs->layout()->id() != rhs->layout()->id())
+ {
+ return false;
+ }
+
+ if (lhs->layout()->batch() != rhs->layout()->batch())
+ {
+ return false;
+ }
+
+ if (!(lhs->layout()->shape() == rhs->layout()->shape()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief A FeatureGroup denotes a group of FeatureObject(s)
+ *
+ * Each FeatureGroup includes at most 1 DEF FeatureObject (a FeatureObject that has a producer),
+ * and may include multiple USE FeatureObject(s) (a FeatureObject that has no producer).
+ *
+ * NOTE FeatureUnification pass internally uses this FeatureGroup to store a group of compatible
+ * FeatureObject(s)
+ */
+class FeatureGroup
+{
+public:
+ explicit FeatureGroup(coco::FeatureObject *feature) { insert(feature); }
+
+public:
+ uint32_t size(void) const { return _uses.size() + (_def ? 1 : 0); }
+
+public:
+ void insert(coco::FeatureObject *feature)
+ {
+ if (feature->def() != nullptr)
+ {
+ assert(_def == nullptr);
+ _def = feature;
+ }
+ else
+ {
+ _uses.insert(feature);
+ }
+ }
+
+public:
+ coco::FeatureObject *parent(void) const
+ {
+ if (_def)
+ {
+ return _def;
+ }
+
+ assert(_uses.size() > 0);
+ return *(_uses.begin());
+ }
+
+public:
+ std::set<coco::FeatureObject *> children(void) const
+ {
+ auto res = _uses;
+ res.erase(parent());
+ return res;
+ }
+
+private:
+ coco::FeatureObject *_def = nullptr;
+ std::set<coco::FeatureObject *> _uses;
+};
+
+} // namespace
+
+namespace enco
+{
+
+void unify_feature(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ std::vector<std::unique_ptr<FeatureGroup>> groups;
+
+ auto assign_group = [&](coco::FeatureObject *feature) {
+ // Find a compatible FeatureGroup
+ FeatureGroup *group = nullptr;
+
+ for (const auto &g : groups)
+ {
+ FeatureGroup *candidate = g.get();
+
+ if (!compatible(candidate->parent(), feature))
+ {
+ continue;
+ }
+
+ group = candidate;
+ break;
+ }
+
+ if (group == nullptr)
+ {
+ // Insert FeatureObject into a new FeatureGroup
+ groups.emplace_back(make_unique<FeatureGroup>(feature));
+ }
+ else
+ {
+ // Insert FeatureObject into the compatible FeatureGroup
+ group->insert(feature);
+ }
+ };
+
+ auto bag = m->entity()->bag()->at(n);
+
+ for (auto o : coco::dependent_objects(bag))
+ {
+ if (auto feature = o->asFeature())
+ {
+ if (candidate(feature))
+ {
+ assign_group(feature);
+ }
+ }
+ }
+
+ for (const auto &g : groups)
+ {
+ auto group = g.get();
+ for (const auto child : group->children())
+ {
+ subst(child, group->parent());
+ assert(child->def() == nullptr);
+ assert(child->uses()->size() == 0);
+ m->entity()->object()->destroy(child);
+ }
+ }
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/FeatureUnification.h b/compiler/enco/core/src/Transforms/FeatureUnification.h
new file mode 100644
index 000000000..5ab0f9d7a
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FeatureUnification.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 __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__
+#define __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Remove duplicated feature objects inside each bag
+ *
+ * >>> BEFORE <<<
+ * %b = Bag(...)
+ *
+ * %feature_0 = Feature(...) at %b
+ * %feature_1 = Feature(...) at %b
+ *
+ * ...
+ * Use(%feature_0)
+ * ...
+ * Use(%feature_1)
+ * ...
+ *
+ * >>> AFTER <<<
+ * %b = Bag(...)
+ *
+ * %feature_0 = Feature(...) at %b
+ * ~~%feature_1 = Feature(...) at %b~~ <- REMOVED
+ *
+ * ...
+ * Use(%feature_0)
+ * ...
+ * Use(%feature_0)
+ * ...
+ *
+ * Note that all the occurrences of "%feature_1" are replaced with "%feature_0"
+ */
+void unify_feature(enco::Code *code);
+
+struct FeatureUnificationPass final : public Pass
+{
+ PASS_CTOR(FeatureUnificationPass)
+ {
+ // DO NOTHING
+ }
+ void run(const SessionID &sess) const override { unify_feature(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_FEATURE_UNIFICATION_H__
diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp b/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp
new file mode 100644
index 000000000..a62324b28
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 "FreeInstrElimination.h"
+
+#include <cassert>
+#include <set>
+
+namespace
+{
+
+/**
+ * @brief Return the set of "free" instructions in a given module
+ */
+std::set<coco::Instr *> free_instrs(const coco::Module *m)
+{
+ std::set<coco::Instr *> res;
+
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ if (auto ins = m->entity()->instr()->at(n))
+ {
+ if (ins->parent() == nullptr)
+ {
+ res.insert(ins);
+ }
+ }
+ }
+
+ return res;
+}
+
+void destroy(coco::Instr *ins)
+{
+ auto m = ins->module();
+ m->entity()->instr()->destroy(ins);
+}
+
+} // namespace
+
+namespace enco
+{
+
+void eliminate_free_instr(coco::Module *m)
+{
+ for (auto ins : free_instrs(m))
+ {
+ destroy(ins);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.h b/compiler/enco/core/src/Transforms/FreeInstrElimination.h
new file mode 100644
index 000000000..1d311cd35
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.h
@@ -0,0 +1,54 @@
+/*
+ * 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 __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__
+#define __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Eliminate free instructions
+ *
+ * An instruction is referred to as "free" if it is not bound to any "block"
+ */
+void eliminate_free_instr(coco::Module *mod);
+
+/**
+ * @brief Eliminate free instructions
+ */
+static inline void eliminate_free_instr(enco::Code *code)
+{
+ // This function is just a wrapper of the above "void eliminate_free_instr(coco::Module *mod)"
+ eliminate_free_instr(code->module());
+}
+
+struct FreeInstrEliminationPass final : public Pass
+{
+ PASS_CTOR(FreeInstrEliminationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { eliminate_free_instr(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_FREE_INSTR_ELIMINATION_H__
diff --git a/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp b/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp
new file mode 100644
index 000000000..c15f32e7d
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeInstrElimination.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "FreeInstrElimination.h"
+
+#include <gtest/gtest.h>
+
+TEST(FreeInstrEliminationTest, case_000)
+{
+ auto m = coco::Module::create();
+
+ // Create a "free" Eval instruction
+ m->entity()->instr()->create<coco::Eval>();
+
+ ASSERT_EQ(m->entity()->instr()->size(), 1);
+
+ // Apply "Free Instruction Elimination"
+ enco::eliminate_free_instr(m.get());
+
+ ASSERT_EQ(m->entity()->instr()->size(), 0);
+}
diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.cpp b/compiler/enco/core/src/Transforms/FreeOpElimination.cpp
new file mode 100644
index 000000000..25f2f44d0
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeOpElimination.cpp
@@ -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.
+ */
+
+#include "FreeOpElimination.h"
+
+#include <cassert>
+#include <set>
+
+namespace
+{
+
+/**
+ * @brief Return the set of Free Op Elimination candidates
+ */
+std::set<coco::Op *> candidates(const coco::Module *m)
+{
+ std::set<coco::Op *> res;
+
+ for (uint32_t n = 0; n < m->entity()->op()->size(); ++n)
+ {
+ if (auto op = m->entity()->op()->at(n))
+ {
+ if ((op->parent() == nullptr) && (op->up() == nullptr))
+ {
+ res.insert(op);
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void eliminate_free_op(coco::Module *m)
+{
+ for (auto op : candidates(m))
+ {
+ m->entity()->op()->destroy_all(op);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.h b/compiler/enco/core/src/Transforms/FreeOpElimination.h
new file mode 100644
index 000000000..3aeacada5
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeOpElimination.h
@@ -0,0 +1,54 @@
+/*
+ * 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 __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__
+#define __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Eliminate free op
+ *
+ * An op is referred to as "free" if it is not bound to any "instruction"
+ */
+void eliminate_free_op(coco::Module *mod);
+
+/**
+ * @brief Eliminate free op
+ */
+static inline void eliminate_free_op(enco::Code *code)
+{
+ // This function is just a wrapper of the above "void eliminate_free_op(coco::Module *mod)"
+ eliminate_free_op(code->module());
+}
+
+struct FreeOpEliminationPass final : public Pass
+{
+ PASS_CTOR(FreeOpEliminationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { eliminate_free_op(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_FREE_OP_ELIMINATION_H__
diff --git a/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp b/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp
new file mode 100644
index 000000000..41600526b
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/FreeOpElimination.test.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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 "FreeOpElimination.h"
+
+#include <gtest/gtest.h>
+
+TEST(FreeOpEliminationTest, case_000)
+{
+ auto m = coco::Module::create();
+
+ // Create a "free" Load op
+ m->entity()->op()->create<coco::Load>();
+
+ ASSERT_EQ(m->entity()->op()->size(), 1);
+
+ // Apply "Free Op Elimination"
+ enco::eliminate_free_op(m.get());
+
+ ASSERT_EQ(m->entity()->op()->size(), 0);
+}
diff --git a/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp
new file mode 100644
index 000000000..152477a51
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/GlobalDataGeneration.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "GlobalDataGeneration.h"
+#include "Split.h"
+#include "Dims.h"
+
+#include <stdex/Memory.h>
+
+#include <map>
+
+using stdex::make_unique;
+
+namespace
+{
+
+/**
+ * @brief Manage global variable declarations
+ */
+class Global
+{
+public:
+ Global(std::ostream &os) : _os(os)
+ {
+ // DO NOTHING
+ }
+
+public:
+ /// @brief Create a global constant string (const char *) literal, and return variable name
+ enco::GlobalOffset constant(const std::string &value);
+
+ /// @brief Create a global constant array variable of type T
+ template <typename T> enco::GlobalOffset constant(const std::vector<T> &values);
+
+ /// @brief Create a global constant array variable of byte (uint8_t) type
+ enco::GlobalOffset constant(const uint8_t *base, uint32_t size);
+
+private:
+ uint32_t _offset = 0;
+ std::ostream &_os;
+};
+
+enco::GlobalOffset Global::constant(const std::string &s)
+{
+ auto const base = reinterpret_cast<const uint8_t *>(s.c_str());
+ auto const size = s.size() + 1 /* NUL */;
+ return constant(base, size);
+}
+
+template <> enco::GlobalOffset Global::constant(const std::vector<uint32_t> &values)
+{
+ auto const base = reinterpret_cast<const uint8_t *>(values.data());
+ auto const size = sizeof(uint32_t) * values.size();
+ return constant(base, size);
+}
+
+enco::GlobalOffset Global::constant(const uint8_t *base, uint32_t size)
+{
+ auto pos = _os.tellp();
+ assert(pos != -1);
+
+ _os.write(reinterpret_cast<const char *>(base), size);
+
+ return static_cast<enco::GlobalOffset>(pos);
+}
+
+} // namespace
+
+namespace
+{
+
+std::map<const ann::Operand *, enco::GlobalOffset> data_offset_ctx;
+std::map<const coco::Bag *, enco::GlobalOffset> bag_data_offset_ctx;
+
+std::map<const coco::Arg *, enco::GlobalOffset> name_offset_ctx;
+std::map<const coco::Arg *, enco::GlobalOffset> dims_offset_ctx;
+
+} // namespace
+
+namespace enco
+{
+
+GlobalOffset GlobalData::data_offset(const ann::Operand *o) { return data_offset_ctx.at(o); }
+
+GlobalOffset GlobalData::data_offset(const coco::Bag *bag)
+{
+ assert(bag_data_offset_ctx.find(bag) != bag_data_offset_ctx.end());
+ return bag_data_offset_ctx.at(bag);
+}
+
+GlobalOffset GlobalData::name_offset(const coco::Input *in) { return name_offset_ctx.at(in); }
+GlobalOffset GlobalData::dims_offset(const coco::Input *in) { return dims_offset_ctx.at(in); }
+
+GlobalOffset GlobalData::name_offset(const coco::Output *out) { return name_offset_ctx.at(out); }
+GlobalOffset GlobalData::dims_offset(const coco::Output *out) { return dims_offset_ctx.at(out); }
+
+void generate_global_data(std::ostream &os, enco::Code *code)
+{
+ auto m = code->module();
+ auto d = code->data();
+
+ auto ann_ctx = enco::SubnetManager::context(m);
+
+ auto global = make_unique<Global>(os);
+
+ //
+ // Emit Bag's weight
+ //
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ if (!d->allocated(bag))
+ {
+ // Skip if the weight value does not exist for a given bag
+ continue;
+ }
+
+ // NOTE The current implementation assumes that all the values are of float(fp32) type
+ // TODO Support non-float values
+ auto span = d->f32()->weight(bag);
+
+ assert(span.data() != nullptr);
+ assert(span.size() > 0);
+
+ auto const base = reinterpret_cast<const uint8_t *>(span.data());
+ uint32_t const size = span.size() * sizeof(float);
+
+ assert(bag_data_offset_ctx.find(bag) == bag_data_offset_ctx.end());
+ bag_data_offset_ctx[bag] = global->constant(base, size);
+ }
+
+ for (uint32_t n = 0; n < ann_ctx->count(); ++n)
+ {
+ auto binder = ann_ctx->nth(n);
+
+ auto emit = [&](const ann::OperandID & /*id*/, const ann::Operand *info) {
+ if (info->weight())
+ {
+ auto base = info->weight()->base();
+ auto size = info->weight()->size();
+
+ data_offset_ctx[info] = global->constant(base, size);
+ }
+ };
+ binder->module()->operand()->each(emit);
+ }
+
+ for (uint32_t n = 0; n < m->input()->size(); ++n)
+ {
+ auto input = m->input()->at(n);
+ auto dims = as_dims(input->shape());
+
+ name_offset_ctx[input] = global->constant(input->name());
+ dims_offset_ctx[input] = global->constant<uint32_t>(dims);
+ }
+
+ for (uint32_t n = 0; n < m->output()->size(); ++n)
+ {
+ auto output = m->output()->at(n);
+ auto dims = as_dims(output->shape());
+
+ name_offset_ctx[output] = global->constant(output->name());
+ dims_offset_ctx[output] = global->constant<uint32_t>(dims);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/GlobalDataGeneration.h b/compiler/enco/core/src/Transforms/GlobalDataGeneration.h
new file mode 100644
index 000000000..433431401
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/GlobalDataGeneration.h
@@ -0,0 +1,54 @@
+/*
+ * 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 __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__
+#define __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__
+
+#include "Code.h"
+
+#include <ostream>
+
+namespace enco
+{
+
+using GlobalOffset = uint32_t;
+
+struct GlobalData
+{
+ static GlobalOffset data_offset(const ann::Operand *);
+ /**
+ * @brief Return the weight offset of a given bag
+ *
+ * @note The behavior of "data_offset" is undefined if a bag has no weight.
+ */
+ static GlobalOffset data_offset(const coco::Bag *);
+
+ static GlobalOffset name_offset(const coco::Input *);
+ static GlobalOffset dims_offset(const coco::Input *);
+ static GlobalOffset name_offset(const coco::Output *);
+ static GlobalOffset dims_offset(const coco::Output *);
+};
+
+/**
+ * @brief Generate 'Global' weight array.
+ *
+ * NOTE Succeeding passes can access offsets via "GlobalData"
+ */
+void generate_global_data(std::ostream &, enco::Code *);
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_GLOBAL_DATA_GENERATION_H__
diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp
new file mode 100644
index 000000000..cb996d2ac
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "IdenticalObjectReduction.h"
+#include "IRUtils.h"
+
+#include <set>
+
+namespace enco
+{
+
+void reduce_identical_object(enco::Code *code)
+{
+ auto m = code->module();
+
+ std::set<coco::Copy *> detached;
+
+ // Preceding optimizations may generate "free" instructions.
+ // - i.e. an instruction not linked to a block
+ //
+ // Let's iterate over only a sequence of "bounded" instructions.
+ for (auto ins : instr_sequence(m))
+ {
+ assert(ins != nullptr);
+ assert(ins->parent() != nullptr);
+
+ auto copy = ins->asCopy();
+
+ if (copy == nullptr)
+ {
+ // Skip if instruction is not a copy
+ continue;
+ }
+
+ // TODO Support non-Feature Objects
+ auto ifm = copy->from()->asFeature();
+ auto ofm = copy->into()->asFeature();
+
+ assert(ofm->bag() != nullptr);
+
+ if (ifm->layout()->id() != ofm->layout()->id())
+ {
+ continue;
+ }
+
+ if (ifm->layout()->id() != coco::FeatureLayouts::BHWC::uid())
+ {
+ continue;
+ }
+
+ // Skip if this copy produces network output
+ if (ofm->bag()->output())
+ {
+ // TODO Optimize this case
+ //
+ // Note that the code under optimization is of the following form:
+ //
+ // %ifm <- Instr(...)
+ // %ofm <- Copy(%ifm)
+ //
+ // Let's assume that "Copy" is the only reader of %ifm (to be precise, its bag).
+ //
+ // Then, it is possible to rewrite the above fragment as follows:
+ //
+ // %ofm <- Instr(...)
+ //
+ continue;
+ }
+
+ if (ofm->bag()->reads()->size() > 0)
+ {
+ // Let us consider the following code:
+ //
+ // Bag:
+ // %bag_0 = Bag(...)
+ // %bag_1 = Bag(...)
+ // %bag_2 = Bag(...)
+ //
+ // Object:
+ // %obj_0 = FeatureObject(bag: %bag_0)
+ // %obj_1 = FeatureObject(bag: %bag_1)
+ //
+ // Instr:
+ // copy an object from %obj_0 into %obj_1
+ // shuffle values from %bag_1 into %bag_2
+ // eval Conv2D with %obj_1
+ //
+ // Identical Object Reduction (IOR) tries to eliminate the first copy via
+ // substitution (substitute all the occurrence of %obj_1 as use with %obj_0).
+ //
+ // Here is the code transformed by IOR:
+ //
+ // Bag:
+ // %bag_0 = Bag(...)
+ // %bag_1 = Bag(...)
+ // %bag_2 = Bag(...)
+ //
+ // Object:
+ // %obj_0 = FeatureObject(bag: %bag_0)
+ // %obj_1 = FeatureObject(bag: %bag_1)
+ //
+ // Instr:
+ // shuffle values from %bag_1 into %bag_2
+ // eval Conv2D with %obj_0
+ //
+ // Note that there is no updater of %bag_1 after IOR, and thus the behavior
+ // of the first shuffle instruction has changed.
+ //
+ // This examples shows that it is impossible to simply substitute %obj_1
+ // with %obj_0 in the presence of readers over its backing bag.
+ continue;
+ }
+
+ subst(copy->into(), copy->from());
+
+ copy->detach();
+ detached.insert(copy);
+ }
+
+ for (auto copy : detached)
+ {
+ m->entity()->instr()->destroy(copy);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h
new file mode 100644
index 000000000..b5bb25d7c
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.h
@@ -0,0 +1,69 @@
+/*
+ * 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 __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__
+#define __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Reduce identically copied objects as its original object
+ *
+ * >>> BEFORE <<<
+ * %bag_0 = Bag(size: N)
+ * %bag_1 = Bag(size: N)
+ *
+ * %obj_0 = Feature(layout: BHWC) at %bag_0
+ * %obj_1 = Feature(layout: BHWC) at %bag_1
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * ...
+ * Use(%obj_0)
+ * Use(%obj_1)
+ * ...
+ *
+ * >>> AFTER <<<
+ * %bag_0 = Bag(size: N)
+ * %bag_1 = Bag(size: N)
+ *
+ * %obj_0 = Feature(layout: BHWC) at %bag_0
+ * %obj_1 = Feature(layout: BHWC) at %bag_1
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * ...
+ * Use(%obj_0)
+ * Use(%obj_0) <- %obj_1 is replaced
+ * ...
+ */
+void reduce_identical_object(enco::Code *code);
+
+struct IdenticalObjectReductionPass final : public Pass
+{
+ PASS_CTOR(IdenticalObjectReductionPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { reduce_identical_object(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_IDENTICAL_OBJECT_REDUCTION_H__
diff --git a/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp
new file mode 100644
index 000000000..772bea08e
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IdenticalObjectReduction.test.cpp
@@ -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 "IdenticalObjectReduction.h"
+
+#include <gtest/gtest.h>
+
+TEST(IdenticalObjectReductionTest, case_000)
+{
+ auto m = coco::Module::create();
+
+ // Create a "free" Eval instruction
+ m->entity()->instr()->create<coco::Eval>();
+
+ enco::Code code{m.get(), nullptr};
+
+ // NOTE This code SHOULD NOT crash
+ enco::reduce_identical_object(&code);
+}
diff --git a/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp b/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp
new file mode 100644
index 000000000..b36620f61
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IndirectCopyElimination.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "IndirectCopyElimination.h"
+
+#include <cassert>
+
+namespace
+{
+
+coco::Copy *as_copy(coco::Instr *ins) { return ins ? ins->asCopy() : nullptr; }
+
+/**
+ * @brief Return a set of copy instructions that are accessible from top-level module
+ */
+std::set<coco::Copy *> linked_copy_instrs(coco::Module *m)
+{
+ std::set<coco::Copy *> res;
+
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ auto ins = m->entity()->instr()->at(n);
+ assert(ins != nullptr);
+
+ if (ins->parent() && ins->parent()->parent())
+ {
+ if (auto copy = ins->asCopy())
+ {
+ res.insert(copy);
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void eliminate_indirect_copy(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (auto child : linked_copy_instrs(m))
+ {
+ auto from = child->from();
+ assert(from != nullptr);
+
+ // Find the irreducible origin
+ while (true)
+ {
+ if (auto producer = coco::producer(from))
+ {
+ if (auto parent = as_copy(producer->loc()))
+ {
+ assert(parent->from() != nullptr);
+ from = parent->from();
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ child->from(from);
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/IndirectCopyElimination.h b/compiler/enco/core/src/Transforms/IndirectCopyElimination.h
new file mode 100644
index 000000000..acfdf569b
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IndirectCopyElimination.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 __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__
+#define __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Convert all the indirect copies as a direct copy
+ *
+ * >>> BEFORE <<<
+ * %obj_0 = ...
+ * %obj_1 = ...
+ * %obj_2 = ...
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * copy(from: %obj_1, into: %obj_2)
+ *
+ * >>> AFTER <<<
+ * %obj_0 = ...
+ * %obj_1 = ...
+ * %obj_2 = ...
+ *
+ * copy(from: %obj_0, into: %obj_1)
+ * copy(from: %obj_0, into: %obj_2)
+ *
+ */
+void eliminate_indirect_copy(enco::Code *code);
+
+struct IndirectCopyEliminationPass final : public enco::Pass
+{
+ PASS_CTOR(IndirectCopyEliminationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { eliminate_indirect_copy(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __ENCO_TRANSFORM_INDIRECT_COPY_ELIMINATION_H__
diff --git a/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp b/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp
new file mode 100644
index 000000000..7bf1c4926
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IntrinsicSelection.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 "IntrinsicSelection.h"
+
+#include "coex/IR.h"
+
+namespace
+{
+
+/**
+ * @brief Return a backend-speicific coco (extend) instruction
+ *
+ * @note rewrite(ins) returns nullptr if selection fails
+ */
+coco::Instr *rewrite(coco::Instr *curr)
+{
+ auto m = curr->module();
+ assert(m != nullptr);
+
+ if (auto eval = coco::safe_cast<coco::Eval>(curr))
+ {
+ if (auto concat_f = eval->op()->asConcatF())
+ {
+ auto fst_load = concat_f->left()->asLoad();
+ auto snd_load = concat_f->right()->asLoad();
+
+ if (fst_load && snd_load && (concat_f->axis() == coco::ConcatF::Axis::Depth))
+ {
+ // Here is the pattern of interest
+ //
+ // %ofm = eval(ConcatF(Depth, Load(%left), Load(%right)))
+ //
+ auto fst_feature = fst_load->object()->asFeature();
+ auto snd_feature = snd_load->object()->asFeature();
+ assert((fst_feature != nullptr) && (snd_feature != nullptr));
+
+ auto out_feature = eval->out()->asFeature();
+ assert(out_feature != nullptr);
+
+ eval->out(nullptr);
+
+ auto depth_concat = m->entity()->instr()->create<ANNDepthConcatF>();
+
+ depth_concat->out(out_feature);
+ depth_concat->fst(fst_feature);
+ depth_concat->snd(snd_feature);
+
+ return depth_concat;
+ }
+
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void select_intrinsic(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (auto blk = m->block()->head(); blk; blk = blk->next())
+ {
+ auto ins = blk->instr()->head();
+
+ while (ins)
+ {
+ if (auto rewritten_ins = rewrite(ins))
+ {
+ rewritten_ins->insertBefore(ins);
+ ins->detach();
+
+ ins = rewritten_ins;
+ }
+
+ ins = ins->next();
+ }
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/IntrinsicSelection.h b/compiler/enco/core/src/Transforms/IntrinsicSelection.h
new file mode 100644
index 000000000..67d38eaeb
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/IntrinsicSelection.h
@@ -0,0 +1,47 @@
+/*
+ * 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 __INTRINSIC_SELECTION_H__
+#define __INTRINSIC_SELECTION_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Select Intricsic (API) to be used
+ *
+ * This pass is analogue of "Instruction Selection" pass. This "Intrisic Selection" pass
+ * will replace a general coco IR instruction into a backend-specific coco (extended) IR
+ * instruction.
+ */
+void select_intrinsic(enco::Code *);
+
+struct IntrinsicSelectionPass final : public Pass
+{
+ PASS_CTOR(IntrinsicSelectionPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { select_intrinsic(code(sess)); }
+};
+
+} // namespace enco
+
+#endif // __INTRINSIC_SELECTION_H__
diff --git a/compiler/enco/core/src/Transforms/Optimizations.cpp b/compiler/enco/core/src/Transforms/Optimizations.cpp
new file mode 100644
index 000000000..7f0974dd0
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Optimizations.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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 "Optimizations.h"
+#include "CodeIndex.h"
+
+#include <cassert>
+
+namespace enco
+{
+
+void generate_bypass_shuffle(enco::Code *code)
+{
+ auto m = code->module();
+
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ // NOTE The current implementation assumes that all the updates occurs before the first read
+ // TODO Remove this assumption
+ for (auto u : coco::updaters(bag))
+ {
+ if ((u->loc() == nullptr) || (u->loc()->asShuffle() == nullptr))
+ {
+ // Skip if updater is not a Shuffle instruction
+ continue;
+ }
+
+ for (auto r : coco::readers(bag))
+ {
+ if ((r->loc() == nullptr) || (r->loc()->asShuffle() == nullptr))
+ {
+ // Skip if reader is not a Shuffle instruction
+ continue;
+ }
+
+ auto shuffle_1 = u->loc()->asShuffle();
+ auto shuffle_2 = r->loc()->asShuffle();
+
+ // Construct a shuffle instruction
+ auto shuffle_3 = m->entity()->instr()->create<coco::Shuffle>();
+
+ shuffle_3->from(shuffle_1->from());
+ shuffle_3->into(shuffle_2->into());
+
+ // Attempt to construct a valid bypass shuffle instruction
+ bool valid = true;
+
+ for (const auto &C : shuffle_2->range())
+ {
+ auto B = shuffle_2->at(C);
+
+ if (!shuffle_1->defined(B))
+ {
+ valid = false;
+ break;
+ }
+
+ auto A = shuffle_1->at(B);
+
+ shuffle_3->insert(A, C);
+ }
+
+ if (valid)
+ {
+ // Insert shuffle_3 before shuffle_2 if shuffle_3 is a valid bypass of shuffle_2
+ shuffle_3->insertBefore(shuffle_2);
+
+ // NOTE shuffle_2 SHOULD BE detached and destroyed after shuffle_3 is inserted
+ shuffle_2->detach();
+ m->entity()->instr()->destroy(shuffle_2);
+ }
+ else
+ {
+ // Destroy shuffle_3 (bypass shuffle) if it is invalid
+ m->entity()->instr()->destroy(shuffle_3);
+ }
+ }
+ }
+ }
+}
+
+} // namespace enco
+
+//
+// Hoist Object
+//
+namespace
+{
+
+bool hoistable(const coco::Shuffle *shuffle)
+{
+ auto range = shuffle->range();
+
+ if (range.size() != shuffle->into()->size())
+ {
+ return false;
+ }
+
+ for (const auto &dst : range)
+ {
+ if (shuffle->at(dst).value() != dst.value())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool complete(const coco::Shuffle *s) { return s->range().size() == s->into()->size(); }
+
+bool compatible(const coco::Shuffle *s1, const coco::Shuffle *s2)
+{
+ if (s1->from() != s2->from())
+ {
+ return false;
+ }
+
+ if (s1->into()->size() != s2->into()->size())
+ {
+ return false;
+ }
+
+ auto range_1 = s1->range();
+ auto range_2 = s2->range();
+
+ if (range_1.size() != range_2.size())
+ {
+ return false;
+ }
+
+ bool res = true;
+
+ for (const auto &dst : range_2)
+ {
+ if (!s1->defined(dst))
+ {
+ res = false;
+ break;
+ }
+
+ auto src_1 = s1->at(dst);
+ auto src_2 = s2->at(dst);
+
+ if (src_1.value() != src_2.value())
+ {
+ res = false;
+ break;
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace enco
+{
+
+void hoist_object(enco::Code *code)
+{
+ auto m = code->module();
+
+ //
+ // Case 1
+ //
+ for (uint32_t n = 0; n < m->entity()->instr()->size(); ++n)
+ {
+ if (auto shuffle = m->entity()->instr()->at(n)->asShuffle())
+ {
+ if (shuffle->parent() == nullptr)
+ {
+ continue;
+ }
+
+ if (hoistable(shuffle))
+ {
+ auto from = shuffle->from();
+ auto into = shuffle->into();
+
+ into->replaceAllDepsWith(from);
+ }
+ }
+ }
+
+ //
+ // Case 2
+ //
+ for (uint32_t n = 0; n < m->entity()->bag()->size(); ++n)
+ {
+ auto bag = m->entity()->bag()->at(n);
+
+ std::map<CodeIndex, coco::Shuffle *> collected;
+
+ for (auto reader : coco::readers(bag))
+ {
+ if (auto ins = reader->loc())
+ {
+ if (auto shuffle = ins->asShuffle())
+ {
+ collected[code_index(shuffle)] = shuffle;
+ }
+ }
+ }
+
+ std::vector<coco::Shuffle *> sorted;
+
+ for (auto it = collected.begin(); it != collected.end(); ++it)
+ {
+ sorted.emplace_back(it->second);
+ }
+
+ for (uint32_t curr = 0; curr < sorted.size(); ++curr)
+ {
+ auto const curr_ins = sorted.at(curr);
+ auto const curr_bag = curr_ins->into();
+
+ if (!complete(curr_ins))
+ {
+ continue;
+ }
+
+ for (uint32_t next = curr + 1; next < sorted.size(); ++next)
+ {
+ auto const next_ins = sorted.at(next);
+ auto const next_bag = next_ins->into();
+
+ if (!complete(next_ins))
+ {
+ continue;
+ }
+
+ if (compatible(curr_ins, next_ins))
+ {
+ next_bag->replaceAllDepsWith(curr_bag);
+ }
+ }
+ }
+ }
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/Optimizations.h b/compiler/enco/core/src/Transforms/Optimizations.h
new file mode 100644
index 000000000..7cfc2305c
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Optimizations.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 __ENCO_OPTIMIZATIONS_H__
+#define __ENCO_OPTIMIZATIONS_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+/**
+ * @brief Add a bypass Shuffle if two continued Shuffles map same from-into
+ *
+ * %bag_1 = Bag(size: N)
+ * %bag_2 = Bag(size: N)
+ * %bag_3 = Bag(size: N)
+ *
+ * >>> BEFORE <<<
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0])
+ * Shuffle(from: %bag_2, into: %bag_3, [0 -> 0])
+ *
+ * Let's refer to the former shuffle as Shuffle 1 and the latter one as Shuffle 2.
+ * We can replace Shuffle 2 with new Shuffle 3 as follows when Shuffle 1 and
+ * Shuffle 2 map to the same position.
+ *
+ * >>> AFTER <<<
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- Shuffle 1
+ * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- Shuffle 3
+ *
+ * Note that Shuffle 1 can be eliminated when %bag_2 is not used
+ */
+void generate_bypass_shuffle(enco::Code *code);
+
+struct BypassGenerationPass final : public Pass
+{
+ PASS_CTOR(BypassGenerationPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { generate_bypass_shuffle(code(sess)); }
+};
+
+/**
+ * @brief Update the base bag of each object if possible
+ *
+ * --- Case 1 ---
+ * Let us consider the following code:
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_1
+ * %obj_2 = ... at %bag_2
+ *
+ * ...
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle
+ * ...
+ *
+ * Note that the content of %bag_2 after shuffle is identical to a part of %bag_1, so
+ * the following code is identical to the above code
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_1
+ * %obj_2 = ... at %bag_1
+ *
+ * ...
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0])
+ * ...
+ *
+ * --- Case 2 ---
+ * Let us consider the following code:
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ * %bag_3 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_2
+ * %obj_2 = ... at %bag_3
+ *
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle_1
+ * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- shuffle_2
+ *
+ * Note that the content of %bag_3 after shuffle_2 is identical to that of %bag_2 after shuffle_1,
+ * so the following code is identical to the above one:
+ *
+ * %bag_1 = Bag(size: 4)
+ * %bag_2 = Bag(size: 1)
+ * %bag_3 = Bag(size: 1)
+ *
+ * %obj_1 = ... at %bag_2
+ * %obj_2 = ... at %bag_2 <- HERE
+ *
+ * Shuffle(from: %bag_1, into: %bag_2, [0 -> 0]) <- shuffle_1
+ * Shuffle(from: %bag_1, into: %bag_3, [0 -> 0]) <- shuffle_2
+ *
+ * "hoist_object" optimization rewrites the former code as the latter one.
+ *
+ * NOTE "hoist_object" DOES NOT change any instruction. It just updates the base bag of objects of
+ * interest.
+ */
+void hoist_object(enco::Code *code);
+
+} // namespace enco
+
+#endif // __ENCO_OPTIMIZATIONS_H__
diff --git a/compiler/enco/core/src/Transforms/Split.cpp b/compiler/enco/core/src/Transforms/Split.cpp
new file mode 100644
index 000000000..b57b8f882
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Split.cpp
@@ -0,0 +1,1233 @@
+/*
+ * 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 "Split.h"
+#include "Usage.h"
+#include "Session.h"
+#include "coex/IR.h"
+
+#include <coco/IR.h>
+
+#include <nncc/core/ADT/kernel/NHWCLayout.h>
+#include <stdex/Memory.h>
+
+#include <map>
+#include <stdexcept>
+#include <functional>
+
+using stdex::make_unique;
+
+namespace
+{
+
+std::map<const coco::Module *, std::unique_ptr<ANNContext>> _subnet_contexts;
+
+} // namespace
+
+namespace enco
+{
+
+const ANNContext *SubnetManager::context(const coco::Module *m)
+{
+ return _subnet_contexts.at(m).get();
+}
+
+} // namespace enco
+
+namespace
+{
+
+using Appender = std::function<void(ANNBinder *binder)>;
+
+struct ANNOpAppender
+{
+ virtual ~ANNOpAppender() = default;
+
+ virtual void append(ANNBinder *binder) const = 0;
+};
+
+class ANNAddAppender final : public ANNOpAppender
+{
+public:
+ void left(coco::FeatureObject *o) { _left = o; }
+ void right(coco::FeatureObject *o) { _right = o; }
+ void out(coco::FeatureObject *o) { _out = o; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto left = binder->addOperand<float>(_left);
+ auto right = binder->addOperand<float>(_right);
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto out = binder->addOperand<float>(_out);
+
+ binder->addOperation(ann::Operation::Code::ADD, {left, right, fuse}, {out});
+ }
+
+private:
+ coco::FeatureObject *_left = nullptr;
+ coco::FeatureObject *_right = nullptr;
+ coco::FeatureObject *_out = nullptr;
+};
+
+class ANNMulAppender final : public ANNOpAppender
+{
+public:
+ void left(coco::FeatureObject *o) { _left = o; }
+ void right(coco::FeatureObject *o) { _right = o; }
+ void out(coco::FeatureObject *o) { _out = o; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto left = binder->addOperand<float>(_left);
+ auto right = binder->addOperand<float>(_right);
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto out = binder->addOperand<float>(_out);
+
+ binder->addOperation(ann::Operation::Code::MUL, {left, right, fuse}, {out});
+ }
+
+private:
+ coco::FeatureObject *_left = nullptr;
+ coco::FeatureObject *_right = nullptr;
+ coco::FeatureObject *_out = nullptr;
+};
+
+/**
+ * WARN The current implementation supports concatenation along depth only
+ */
+class ANNConcatAppender final : public ANNOpAppender
+{
+public:
+ void left(coco::FeatureObject *o) { _left = o; }
+ void right(coco::FeatureObject *o) { _right = o; }
+ void out(coco::FeatureObject *o) { _out = o; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto left = binder->addOperand<float>(_left);
+ auto right = binder->addOperand<float>(_right);
+ auto axis = binder->addOperand<int32_t>();
+ binder->setOperand(axis, 3 /* DEPTH */);
+
+ auto out = binder->addOperand<float>(_out);
+
+ binder->addOperation(ann::Operation::Code::CONCAT, {left, right, axis}, {out});
+ }
+
+private:
+ coco::FeatureObject *_left = nullptr;
+ coco::FeatureObject *_right = nullptr;
+ coco::FeatureObject *_out = nullptr;
+};
+
+class ANNConv2DAppender final : public ANNOpAppender
+{
+public:
+ void session(const enco::SessionID &sess) { _sess = sess; }
+
+ void pad(const coco::Padding2D *pad) { _pad = *pad; }
+ void stride(const coco::Stride2D *stride) { _stride = *stride; }
+
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ker(coco::KernelObject *ker) { _ker = ker; }
+ // Q: Should we take a bias as a feature object?
+ // NOTE This interface is subject to change
+ void bias(coco::FeatureObject *bias) { _bias = bias; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto data = enco::data(_sess);
+
+ auto ifm = binder->addOperand<float>(_ifm);
+ auto ker = binder->addOperand<float>(_ker);
+
+ // Fill kernel data
+ {
+ auto ker_bag = _ker->bag();
+ auto ker_weight = data->f32()->weight(ker_bag);
+
+ assert(ker_weight.data() != nullptr);
+
+ binder->setOperand(ker, ker_weight.data(), ker_weight.data() + ker_weight.size());
+ }
+
+ // Conv2D in coco IR has no bias, but bias is mandatory in Android NN API
+ auto bias = binder->addOperand<float>(nncc::core::ADT::tensor::Shape{_ker->shape().count()});
+
+ // Fill bias data
+ if (_bias == nullptr)
+ {
+ // Use a fresh empty bias if "bias" is not specified
+ auto length = _ker->shape().count();
+
+ std::vector<float> values;
+ values.resize(length, 0.0f);
+
+ binder->setOperand(bias, values.begin(), values.end());
+ }
+ else
+ {
+ // Use specified "bias"
+ auto bias_bag = _bias->bag();
+ auto bias_weight = data->f32()->weight(bias_bag);
+
+ assert(bias_weight.data() != nullptr);
+ assert(bias_weight.size() == _ker->shape().count());
+
+ binder->setOperand(bias, bias_weight.data(), bias_weight.data() + bias_weight.size());
+ }
+
+ auto left = binder->addOperand<int32_t>();
+ binder->setOperand(left, _pad.left());
+ auto right = binder->addOperand<int32_t>();
+ binder->setOperand(right, _pad.right());
+ auto top = binder->addOperand<int32_t>();
+ binder->setOperand(top, _pad.top());
+ auto bottom = binder->addOperand<int32_t>();
+ binder->setOperand(bottom, _pad.bottom());
+ auto hstride = binder->addOperand<int32_t>();
+ binder->setOperand(hstride, _stride.horizontal());
+ auto vstride = binder->addOperand<int32_t>();
+ binder->setOperand(vstride, _stride.vertical());
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::CONV_2D,
+ {ifm, ker, bias, left, right, top, bottom, hstride, vstride, fuse}, {ofm});
+ }
+
+private:
+ enco::SessionID _sess;
+
+private:
+ coco::Padding2D _pad;
+ coco::Stride2D _stride;
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::KernelObject *_ker = nullptr;
+ coco::FeatureObject *_bias = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNDepthwiseConv2DAppender final : public ANNOpAppender
+{
+public:
+ void session(const enco::SessionID &sess) { _sess = sess; }
+
+ void multiplier(const uint32_t &multiplier) { _multiplier = multiplier; }
+ void pad(const coco::Padding2D *pad) { _pad = *pad; }
+ void stride(const coco::Stride2D *stride) { _stride = *stride; }
+
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ker(coco::KernelObject *ker) { _ker = ker; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ using namespace nncc::core::ADT;
+
+ auto data = enco::data(_sess);
+
+ const uint32_t ker_N = _ker->shape().count();
+ const uint32_t ker_H = _ker->shape().height();
+ const uint32_t ker_W = _ker->shape().width();
+
+ assert(ker_N % _multiplier == 0);
+ const uint32_t group = ker_N / _multiplier;
+
+ auto ifm = binder->addOperand<float>(_ifm);
+ auto ker = binder->addOperand<float>(tensor::Shape{1, ker_H, ker_W, ker_N});
+
+ // Fill kernel data
+ {
+ auto obj = _ker;
+ auto shape = obj->shape();
+
+ auto ovl = data->f32()->read(obj);
+ assert(ovl != nullptr);
+
+ // Flatten?
+ std::vector<float> values;
+
+ /**
+ * Android NN computes DEPTHWISE_CONV_2D as follows:
+ *
+ * output[b, i, j, k * channel_multiplier + q] =
+ * sum_{di, dj} (
+ * input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+ * filter[1, di, dj, k * channel_multiplier + q]
+ * ) + bias[k * channel_multiplier + q]
+ *
+ */
+ for (uint32_t row = 0; row < shape.height(); ++row)
+ {
+ for (uint32_t col = 0; col < shape.width(); ++col)
+ {
+ for (uint32_t g = 0; g < group; ++g)
+ {
+ for (uint32_t m = 0; m < _multiplier; ++m)
+ {
+ const auto value = ovl->at(g * _multiplier + m, 0, row, col);
+ values.emplace_back(value);
+ }
+ }
+ }
+ }
+
+ assert(values.size() == nncc::core::ADT::kernel::num_elements(shape));
+ binder->setOperand(ker, values.begin(), values.end());
+ }
+
+ // Conv2D in coco IR has no bias, but bias is mandatory in Android NN API
+ auto bias = binder->addOperand<float>(nncc::core::ADT::tensor::Shape{_ker->shape().count()});
+
+ // Fill bias data
+ {
+ auto length = _ker->shape().count();
+
+ std::vector<float> values;
+ values.resize(length, 0.0f);
+
+ binder->setOperand(bias, values.begin(), values.end());
+ }
+
+ auto left = binder->addOperand<int32_t>();
+ binder->setOperand(left, _pad.left());
+ auto right = binder->addOperand<int32_t>();
+ binder->setOperand(right, _pad.right());
+ auto top = binder->addOperand<int32_t>();
+ binder->setOperand(top, _pad.top());
+ auto bottom = binder->addOperand<int32_t>();
+ binder->setOperand(bottom, _pad.bottom());
+ auto hstride = binder->addOperand<int32_t>();
+ binder->setOperand(hstride, _stride.horizontal());
+ auto vstride = binder->addOperand<int32_t>();
+ binder->setOperand(vstride, _stride.vertical());
+ auto multiplier = binder->addOperand<int32_t>();
+ binder->setOperand(multiplier, _multiplier);
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(
+ ann::Operation::Code::DEPTHWISE_CONV_2D,
+ {ifm, ker, bias, left, right, top, bottom, hstride, vstride, multiplier, fuse}, {ofm});
+ }
+
+private:
+ enco::SessionID _sess;
+
+private:
+ uint32_t _multiplier;
+ coco::Padding2D _pad;
+ coco::Stride2D _stride;
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::KernelObject *_ker = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNReLUAppender final : public ANNOpAppender
+{
+public:
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto ifm = binder->addOperand<float>(_ifm);
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::RELU, {ifm}, {ofm});
+ }
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNReLU6Appender final : public ANNOpAppender
+{
+public:
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto ifm = binder->addOperand<float>(_ifm);
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::RELU6, {ifm}, {ofm});
+ }
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNMaxPool2DAppender final : public ANNOpAppender
+{
+public:
+ void pad(const coco::Padding2D *pad) { _pad = *pad; }
+ void stride(const coco::Stride2D *stride) { _stride = *stride; }
+ void window(const coco::Window2D *window) { _window = *window; }
+
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto ifm = binder->addOperand<float>(_ifm);
+
+ // Set padding
+ auto left = binder->addOperand<int32_t>();
+ binder->setOperand(left, _pad.left());
+ auto right = binder->addOperand<int32_t>();
+ binder->setOperand(right, _pad.right());
+ auto top = binder->addOperand<int32_t>();
+ binder->setOperand(top, _pad.top());
+ auto bottom = binder->addOperand<int32_t>();
+ binder->setOperand(bottom, _pad.bottom());
+
+ // Set horizontal/vertical stride
+ auto hstride = binder->addOperand<int32_t>();
+ binder->setOperand(hstride, _stride.horizontal());
+ auto vstride = binder->addOperand<int32_t>();
+ binder->setOperand(vstride, _stride.vertical());
+
+ // Set receptive field size
+ auto width = binder->addOperand<int32_t>();
+ binder->setOperand(width, _window.width());
+ auto height = binder->addOperand<int32_t>();
+ binder->setOperand(height, _window.height());
+
+ // Set fuse code
+ // TODO Suport operation fusion
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::MAX_POOL_2D,
+ {ifm, left, right, top, bottom, hstride, vstride, width, height, fuse},
+ {ofm});
+ }
+
+private:
+ coco::Padding2D _pad;
+ coco::Stride2D _stride;
+ coco::Window2D _window;
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNAvgPool2DAppender final : public ANNOpAppender
+{
+public:
+ void pad(const coco::Padding2D *pad) { _pad = *pad; }
+ void stride(const coco::Stride2D *stride) { _stride = *stride; }
+ void window(const coco::Window2D *window) { _window = *window; }
+
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto ifm = binder->addOperand<float>(_ifm);
+
+ // Set padding
+ auto left = binder->addOperand<int32_t>();
+ binder->setOperand(left, _pad.left());
+ auto right = binder->addOperand<int32_t>();
+ binder->setOperand(right, _pad.right());
+ auto top = binder->addOperand<int32_t>();
+ binder->setOperand(top, _pad.top());
+ auto bottom = binder->addOperand<int32_t>();
+ binder->setOperand(bottom, _pad.bottom());
+
+ // Set horizontal/vertical stride
+ auto hstride = binder->addOperand<int32_t>();
+ binder->setOperand(hstride, _stride.horizontal());
+ auto vstride = binder->addOperand<int32_t>();
+ binder->setOperand(vstride, _stride.vertical());
+
+ // Set receptive field size
+ auto width = binder->addOperand<int32_t>();
+ binder->setOperand(width, _window.width());
+ auto height = binder->addOperand<int32_t>();
+ binder->setOperand(height, _window.height());
+
+ // Set fuse code
+ // TODO Suport operation fusion
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::AVG_POOL_2D,
+ {ifm, left, right, top, bottom, hstride, vstride, width, height, fuse},
+ {ofm});
+ }
+
+private:
+ coco::Padding2D _pad;
+ coco::Stride2D _stride;
+ coco::Window2D _window;
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNPadFAppender final : public ANNOpAppender
+{
+public:
+ void pad(const coco::Padding2D *pad) { _pad = *pad; }
+
+public:
+ void ifm(coco::FeatureObject *ifm) { _ifm = ifm; }
+ void ofm(coco::FeatureObject *ofm) { _ofm = ofm; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ using nncc::core::ADT::tensor::Shape;
+
+ auto ifm = binder->addOperand<float>(_ifm);
+ auto pad = binder->addOperand<int32_t>(Shape{4, 2});
+ {
+ std::vector<int32_t> values;
+ values.resize(8);
+ // For 'N'
+ values.at(0) = values.at(1) = 0;
+ // For 'H'
+ values.at(2) = _pad.top();
+ values.at(3) = _pad.bottom();
+ // For 'W'
+ values.at(4) = _pad.left();
+ values.at(5) = _pad.right();
+ // For 'C'
+ values.at(6) = values.at(7) = 0;
+
+ binder->setOperand(pad, values.begin(), values.end());
+ }
+
+ auto ofm = binder->addOperand<float>(_ofm);
+
+ binder->addOperation(ann::Operation::Code::PAD, {ifm, pad}, {ofm});
+ }
+
+private:
+ coco::Padding2D _pad;
+
+private:
+ coco::FeatureObject *_ifm = nullptr;
+ coco::FeatureObject *_ofm = nullptr;
+};
+
+class ANNOpFunctionalAppender final : public ANNOpAppender
+{
+public:
+ ANNOpFunctionalAppender(const Appender &fun) : _fun{fun}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void append(ANNBinder *binder) const { _fun(binder); }
+
+private:
+ Appender _fun;
+};
+
+class ANNSubAppender final : public ANNOpAppender
+{
+public:
+ void left(coco::FeatureObject *o) { _left = o; }
+ void right(coco::FeatureObject *o) { _right = o; }
+ void out(coco::FeatureObject *o) { _out = o; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto left = binder->addOperand<float>(_left);
+ auto right = binder->addOperand<float>(_right);
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto out = binder->addOperand<float>(_out);
+
+ binder->addOperation(ann::Operation::Code::SUB, {left, right, fuse}, {out});
+ }
+
+private:
+ coco::FeatureObject *_left = nullptr;
+ coco::FeatureObject *_right = nullptr;
+ coco::FeatureObject *_out = nullptr;
+};
+
+class ANNDivAppender final : public ANNOpAppender
+{
+public:
+ void left(coco::FeatureObject *o) { _left = o; }
+ void right(coco::FeatureObject *o) { _right = o; }
+ void out(coco::FeatureObject *o) { _out = o; }
+
+public:
+ void append(ANNBinder *binder) const override
+ {
+ auto left = binder->addOperand<float>(_left);
+ auto right = binder->addOperand<float>(_right);
+ auto fuse = binder->addOperand<int32_t>();
+ binder->setOperand(fuse, 0);
+
+ auto out = binder->addOperand<float>(_out);
+
+ binder->addOperation(ann::Operation::Code::DIV, {left, right, fuse}, {out});
+ }
+
+private:
+ coco::FeatureObject *_left = nullptr;
+ coco::FeatureObject *_right = nullptr;
+ coco::FeatureObject *_out = nullptr;
+};
+
+class ANNOpBuilder : public coco::Instr::Visitor<std::unique_ptr<ANNOpAppender>>
+{
+public:
+ std::unique_ptr<ANNOpAppender> visit(const coco::Eval *eval)
+ {
+ if (auto conv = eval->op()->asConv2D())
+ {
+ if (auto load = conv->arg()->asLoad())
+ {
+ auto sess = enco::session(eval->module());
+
+ auto ifm = load->object()->asFeature();
+ auto ker = conv->ker();
+ auto ofm = eval->out()->asFeature();
+
+ const auto group = conv->group();
+
+ if (group == 1)
+ {
+ auto app = make_unique<ANNConv2DAppender>();
+
+ app->session(sess);
+
+ app->pad(conv->pad());
+ app->stride(conv->stride());
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+ app->ker(ker);
+
+ return std::move(app);
+ }
+ else
+ {
+ assert(ifm->shape().depth() == group);
+ assert(ker->shape().count() % group == 0);
+ assert(ker->shape().depth() == 1);
+
+ auto app = make_unique<ANNDepthwiseConv2DAppender>();
+
+ app->session(sess);
+
+ app->multiplier(ker->shape().count() / group);
+ app->pad(conv->pad());
+ app->stride(conv->stride());
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+ app->ker(ker);
+
+ return std::move(app);
+ }
+ }
+ }
+ else if (auto op = eval->op()->asAdd())
+ {
+ auto left_load = op->left()->asLoad();
+ auto right_load = op->right()->asLoad();
+
+ if (left_load && right_load)
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(Add(Load(%left), Load(%right)))
+ //
+ auto left = left_load->object()->asFeature();
+ auto right = right_load->object()->asFeature();
+ assert(left != nullptr && right != nullptr);
+
+ auto out = eval->out()->asFeature();
+ assert(out != nullptr);
+
+ auto app = make_unique<ANNAddAppender>();
+
+ app->left(left);
+ app->right(right);
+ app->out(out);
+
+ return std::move(app);
+ }
+ }
+ else if (auto op = eval->op()->asMul())
+ {
+ auto left_load = op->left()->asLoad();
+ auto right_load = op->right()->asLoad();
+
+ if (left_load && right_load)
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(Mul(Load(%left), Load(%right)))
+ //
+ auto left = left_load->object()->asFeature();
+ auto right = right_load->object()->asFeature();
+ assert(left != nullptr && right != nullptr);
+
+ auto out = eval->out()->asFeature();
+ assert(out != nullptr);
+
+ auto app = make_unique<ANNMulAppender>();
+
+ app->left(left);
+ app->right(right);
+ app->out(out);
+
+ return std::move(app);
+ }
+ }
+ else if (auto op = eval->op()->asPadF())
+ {
+ if (auto load = op->arg()->asLoad())
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(PadF(Load(%ifm))
+ //
+ auto ifm = load->object()->asFeature();
+ auto ofm = eval->out()->asFeature();
+
+ assert(ifm != nullptr && ofm != nullptr);
+
+ auto app = make_unique<ANNPadFAppender>();
+
+ app->pad(op->pad());
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+
+ return std::move(app);
+ }
+ }
+ else if (auto maxpool = eval->op()->asMaxPool2D())
+ {
+ if (auto load = maxpool->arg()->asLoad())
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(MaxPool2D(Load(%ifm))
+ //
+ auto ifm = load->object()->asFeature();
+ auto ofm = eval->out()->asFeature();
+
+ assert(ifm != nullptr && ofm != nullptr);
+
+ auto app = make_unique<ANNMaxPool2DAppender>();
+
+ app->pad(maxpool->pad());
+ app->stride(maxpool->stride());
+ app->window(maxpool->window());
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+
+ return std::move(app);
+ }
+ }
+ else if (auto avgpool = eval->op()->asAvgPool2D())
+ {
+ if (auto load = avgpool->arg()->asLoad())
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(AvgPool2D(Load(%ifm))
+ //
+ if (avgpool->divisor() == coco::AvgPool2D::Divisor::PaddingExcluded)
+ {
+ // When ANN runtime computes the average of each receptive field,
+ // it uses the number of valid(=non-padding) elements as a divisor.
+ auto ifm = load->object()->asFeature();
+ auto ofm = eval->out()->asFeature();
+
+ assert(ifm != nullptr && ofm != nullptr);
+
+ auto app = make_unique<ANNAvgPool2DAppender>();
+
+ app->pad(avgpool->pad());
+ app->stride(avgpool->stride());
+ app->window(avgpool->window());
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+
+ return std::move(app);
+ }
+ }
+ }
+ else if (auto relu = eval->op()->asReLU())
+ {
+ if (auto load = relu->arg()->asLoad())
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(ReLU(Load(%ifm))
+ //
+ // TODO Support objects of other kinds, such as Tensor
+ auto ifm = load->object()->asFeature();
+ auto ofm = eval->out()->asFeature();
+
+ assert(ifm != nullptr && ofm != nullptr);
+
+ auto app = make_unique<ANNReLUAppender>();
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+
+ return std::move(app);
+ }
+ }
+ else if (auto relu6 = eval->op()->asReLU6())
+ {
+ if (auto load = relu6->arg()->asLoad())
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(ReLU6(Load(%ifm))
+ //
+ // TODO Support objects of other kinds, such as Tensor
+ auto ifm = load->object()->asFeature();
+ auto ofm = eval->out()->asFeature();
+
+ assert(ifm != nullptr && ofm != nullptr);
+
+ auto app = make_unique<ANNReLU6Appender>();
+
+ app->ifm(ifm);
+ app->ofm(ofm);
+
+ return std::move(app);
+ }
+ }
+ else if (auto op = eval->op()->asConcatF())
+ {
+ auto left_load = op->left()->asLoad();
+ auto right_load = op->right()->asLoad();
+
+ if (left_load && right_load && (op->axis() == coco::ConcatF::Axis::Depth))
+ {
+ // Let's compile the following code fragment:
+ //
+ // %ofm = eval(ConcatF(Depth, Load(%left), Load(%right)))
+ //
+ auto left = left_load->object()->asFeature();
+ auto right = right_load->object()->asFeature();
+ assert(left != nullptr && right != nullptr);
+
+ auto out = eval->out()->asFeature();
+ assert(out != nullptr);
+
+ auto app = make_unique<ANNConcatAppender>();
+
+ app->left(left);
+ app->right(right);
+ app->out(out);
+
+ return std::move(app);
+ }
+ }
+ else if (auto op = eval->op()->asSub())
+ {
+ auto left_load = op->left()->asLoad();
+ auto right_load = op->right()->asLoad();
+
+ if (left_load && right_load)
+ {
+ // Let's compile the following code fragment:
+ //
+ // %out = eval(Sub(Load(%left), Load(%right)))
+ //
+ auto left = left_load->object()->asFeature();
+ auto right = right_load->object()->asFeature();
+ assert(left != nullptr && right != nullptr);
+
+ auto out = eval->out()->asFeature();
+ assert(out != nullptr);
+
+ auto app = make_unique<ANNSubAppender>();
+
+ app->left(left);
+ app->right(right);
+ app->out(out);
+
+ return std::move(app);
+ }
+ }
+ else if (auto op = eval->op()->asDiv())
+ {
+ auto left_load = op->left()->asLoad();
+ auto right_load = op->right()->asLoad();
+
+ if (left_load && right_load)
+ {
+ // Let's compile the following code fragment:
+ //
+ // %out = eval(Div(Load(%left), Load(%right)))
+ //
+ auto left = left_load->object()->asFeature();
+ auto right = right_load->object()->asFeature();
+ assert(left != nullptr && right != nullptr);
+
+ auto out = eval->out()->asFeature();
+ assert(out != nullptr);
+
+ auto app = make_unique<ANNDivAppender>();
+
+ app->left(left);
+ app->right(right);
+ app->out(out);
+
+ return std::move(app);
+ }
+ }
+
+ // Return nullptr if a given Eval instruction is incompatible
+ return nullptr;
+ }
+
+public:
+ std::unique_ptr<ANNOpAppender> visit(const coco::Shuffle *) { return nullptr; }
+};
+
+namespace
+{
+
+std::unique_ptr<ANNOpAppender> make_appender(coco::Instr *ins)
+{
+ ANNOpBuilder op_builder;
+
+ if (auto eval = coco::safe_cast<coco::Eval>(ins))
+ {
+ return eval->accept(op_builder);
+ }
+
+ if (auto depth_concat = coco::safe_cast<ANNDepthConcatF>(ins))
+ {
+ auto app = make_unique<ANNConcatAppender>();
+
+ app->out(depth_concat->out()->asFeature());
+
+ app->left(depth_concat->fst()->asFeature());
+ app->right(depth_concat->snd()->asFeature());
+
+ return std::move(app);
+ }
+
+ // Build ANN IR from ANNConv2D instruction
+ if (auto conv2d = coco::safe_cast<ANNConv2D>(ins))
+ {
+ auto sess = enco::session(conv2d->module());
+ auto app = make_unique<ANNConv2DAppender>();
+
+ app->session(sess);
+
+ app->pad(conv2d->pad());
+ app->stride(conv2d->stride());
+
+ app->ofm(conv2d->ofm()->asFeature());
+ app->ifm(conv2d->ifm()->asFeature());
+ app->ker(conv2d->ker()->asKernel());
+ app->bias(coco::safe_cast<coco::FeatureObject>(conv2d->bias()));
+
+ return std::move(app);
+ }
+
+ return nullptr;
+}
+
+enum Compatibility
+{
+ COMPATIBLE,
+ INCOMPATIBLE
+};
+
+class ANNGroupBuilder
+{
+public:
+ ANNGroupBuilder(ANNContext *ctx) : _ctx{ctx}
+ {
+ // DO NOTHING
+ }
+
+public:
+ Compatibility kind(const coco::Block *blk) const;
+ Compatibility kind(const std::unique_ptr<ANNOpAppender> &appender) const;
+
+public:
+ void build(enco::Code *code) const;
+
+private:
+ ANNContext *_ctx;
+};
+
+Compatibility ANNGroupBuilder::kind(const std::unique_ptr<ANNOpAppender> &app) const
+{
+ return app ? COMPATIBLE : INCOMPATIBLE;
+}
+
+Compatibility ANNGroupBuilder::kind(const coco::Block *blk) const
+{
+ return (_ctx->find(blk) != nullptr) ? COMPATIBLE : INCOMPATIBLE;
+}
+
+void ANNGroupBuilder::build(enco::Code *code) const
+{
+ auto m = code->module();
+
+ // ANNGroupBuilder will construct a sequence of blocks from the original block sequence, and
+ // a destination block (that dst_blk points to) is the tail of the generated sequence.
+ coco::Block *dst_blk = nullptr;
+
+ auto append = [&](const Compatibility &t) {
+ auto blk = m->entity()->block()->create();
+
+ if (dst_blk == nullptr)
+ {
+ m->block()->prepend(blk);
+ }
+ else
+ {
+ blk->insertAfter(dst_blk);
+ }
+
+ dst_blk = blk;
+
+ if (COMPATIBLE == t)
+ {
+ _ctx->create(blk);
+ }
+ };
+
+ for (auto blk = m->block()->head(); blk;)
+ {
+ // Let's move instructions from a block of interest (referred to as source block) into
+ // a destination block
+ auto src_blk = blk;
+ blk = src_blk->next();
+ src_blk->detach();
+
+ for (auto ins = src_blk->instr()->head(); ins;)
+ {
+ auto cur_ins = ins;
+ ins = cur_ins->next();
+ cur_ins->detach();
+
+ auto cur_append = make_appender(cur_ins);
+
+ // Create a new compatible block and use it as a destination block if the current
+ // destination block is absent or incompatible with the instruction of intereset.
+ if ((dst_blk == nullptr) || (kind(cur_append) != kind(dst_blk)))
+ {
+ append(kind(cur_append));
+ }
+
+ assert(dst_blk != nullptr);
+ assert(kind(cur_append) == kind(dst_blk));
+
+ // Append ins to the dst_blk block
+ dst_blk->instr()->append(cur_ins);
+
+ if (cur_append)
+ {
+ // Update Android NN IR if the current instruction is compatible
+ auto binder = _ctx->find(dst_blk);
+ assert(binder != nullptr);
+ cur_append->append(binder);
+ }
+ }
+
+ // Destroy the source block
+ assert(src_blk->instr()->empty());
+ m->entity()->block()->destroy(src_blk);
+ }
+}
+
+} // namespace
+
+class ANNModuleBuilder
+{
+private:
+ std::set<coco::Bag *> inputs(ANNBinder *binder) const;
+ std::set<coco::Bag *> outputs(ANNBinder *binder) const;
+
+public:
+ void build(ANNContext *ann_ctx) const;
+};
+
+std::set<coco::Bag *> ANNModuleBuilder::inputs(ANNBinder *binder) const
+{
+ std::set<coco::Bag *> res;
+
+ for (auto bag : binder->bags())
+ {
+ auto u = enco::updaters(bag);
+ u.erase(binder->block());
+
+ /**
+ * A bag is the input of this block if
+ * 1. it is an input of the whole network, or
+ * 2. it is updated by preceding blocks during execution
+ */
+ if (bag->isInput() || (u.size() > 0))
+ {
+ res.insert(bag);
+ }
+ }
+
+ return res;
+}
+
+std::set<coco::Bag *> ANNModuleBuilder::outputs(ANNBinder *binder) const
+{
+ std::set<coco::Bag *> res;
+
+ for (auto bag : binder->bags())
+ {
+ auto u = enco::updaters(bag);
+ auto r = enco::readers(bag);
+ r.erase(binder->block());
+
+ /**
+ * Only a bag that this block updates can be the output of this block
+ */
+ if (u.find(binder->block()) == u.end())
+ {
+ continue;
+ }
+
+ /**
+ * A bag is the output of this block if
+ * 1. it is an output of the whole network, or
+ * 2. it is read by following blocks during execution
+ */
+ if (bag->isOutput() || (r.size() > 0))
+ {
+ res.insert(bag);
+ }
+ }
+
+ return res;
+}
+
+void ANNModuleBuilder::build(ANNContext *ann_ctx) const
+{
+ for (uint32_t n = 0; n < ann_ctx->count(); ++n)
+ {
+ auto binder = ann_ctx->nth(n);
+
+ // NOTE binder->module() returns an ANN IR module (not coco IR module)
+ auto m = binder->block()->module();
+ auto d = enco::data(m);
+
+ // Let's identify operands with initial values
+ for (auto bag : binder->bags())
+ {
+ if (binder->associated(bag) && d->allocated(bag))
+ {
+ // TODO Support other datatype
+ auto span = d->f32()->weight(bag);
+ assert(span.data() != nullptr);
+
+ binder->setOperand(binder->operand(bag), span.data(), span.data() + span.size());
+ }
+ }
+
+ // Let's identify input/output bags
+ binder->identifyInputs(inputs(binder));
+ binder->identifyOutputs(outputs(binder));
+ }
+}
+
+} // namespace
+
+namespace
+{
+
+class SplitPass
+{
+public:
+ void runOnCode(enco::Code *code) const;
+};
+
+void SplitPass::runOnCode(enco::Code *code) const
+{
+ auto ann_ctx = make_unique<ANNContext>();
+
+ ANNGroupBuilder group_builder{ann_ctx.get()};
+ group_builder.build(code);
+
+ ANNModuleBuilder module_builder;
+ module_builder.build(ann_ctx.get());
+
+ _subnet_contexts[code->module()] = std::move(ann_ctx);
+}
+
+} // namespace
+
+namespace enco
+{
+
+void split_into_phases(enco::Code *code)
+{
+ SplitPass split;
+ split.runOnCode(code);
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Transforms/Split.h b/compiler/enco/core/src/Transforms/Split.h
new file mode 100644
index 000000000..b4e1d7baf
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/Split.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __SPLIT_H__
+#define __SPLIT_H__
+
+#include "Code.h"
+#include "Pass.h"
+
+namespace enco
+{
+
+struct SubnetManager
+{
+ static const ANNContext *context(const coco::Module *m);
+};
+
+/**
+ * @brief Split instructions into a set of phases
+ */
+void split_into_phases(enco::Code *code);
+
+struct PhaseConstructionPass final : public Pass
+{
+ PASS_CTOR(PhaseConstructionPass)
+ {
+ // DO NOTHING
+ }
+
+ void run(const SessionID &sess) const override { split_into_phases(code(sess)); }
+};
+
+} // namespace enco;
+
+#endif // __SPLIT_H__
diff --git a/compiler/enco/core/src/Usage.cpp b/compiler/enco/core/src/Usage.cpp
new file mode 100644
index 000000000..92ccba5a0
--- /dev/null
+++ b/compiler/enco/core/src/Usage.cpp
@@ -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.
+ */
+
+#include "Usage.h"
+
+namespace enco
+{
+
+std::set<coco::Block *> readers(const coco::Bag *bag)
+{
+ std::set<coco::Block *> res;
+
+ for (auto read : coco::readers(bag))
+ {
+ assert(read != nullptr);
+ auto instr = read->loc();
+ assert(instr != nullptr);
+ auto block = instr->parent();
+ assert(block != nullptr);
+
+ res.insert(block);
+ }
+
+ return res;
+}
+
+std::set<coco::Block *> updaters(const coco::Bag *bag)
+{
+ std::set<coco::Block *> res;
+
+ for (auto update : coco::updaters(bag))
+ {
+ assert(update != nullptr);
+ auto instr = update->loc();
+ assert(instr != nullptr);
+ auto block = instr->parent();
+ assert(block != nullptr);
+
+ res.insert(block);
+ }
+
+ return res;
+}
+
+} // namespace enco
diff --git a/compiler/enco/core/src/Usage.h b/compiler/enco/core/src/Usage.h
new file mode 100644
index 000000000..8fa05f9b9
--- /dev/null
+++ b/compiler/enco/core/src/Usage.h
@@ -0,0 +1,34 @@
+/*
+ * 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 __ENCO_USAGE_H__
+#define __ENCO_USAGE_H__
+
+#include "coco/IR.h"
+
+#include <set>
+
+namespace enco
+{
+
+/// @brief Returns the set of blocks that reads a given bag
+std::set<coco::Block *> readers(const coco::Bag *bag);
+/// @brief Return the set of blocks that updates a given bag
+std::set<coco::Block *> updaters(const coco::Bag *bag);
+
+} // namespace enco
+
+#endif // __ENCO_USAGE_H__
diff --git a/compiler/enco/core/src/coex/IR.h b/compiler/enco/core/src/coex/IR.h
new file mode 100644
index 000000000..e81943f18
--- /dev/null
+++ b/compiler/enco/core/src/coex/IR.h
@@ -0,0 +1,109 @@
+/*
+ * 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 __ENCO_COEX_IR_H__
+#define __ENCO_COEX_IR_H__
+
+#include <coco/IR.h>
+
+/**
+ * @brief 2D Convolution through Andoird NN API
+ *
+ * TODO Support FusedActivation
+ */
+class ANNConv2D : public coco::Instr, public coco::Object::Producer, public coco::Object::Consumer
+{
+public:
+ ANNConv2D() : _ofm{this}, _ifm{this}, _ker{this}, _bias{this}
+ {
+ // DO NOTHING
+ }
+
+public:
+ coco::Instr *loc(void) override { return this; }
+
+public:
+ coco::Object *ofm(void) const { return _ofm.value(); }
+ void ofm(coco::Object *o) { _ofm.value(o); }
+
+ coco::Object *ifm(void) const { return _ifm.value(); }
+ void ifm(coco::Object *o) { _ifm.value(o); }
+
+ coco::Object *ker(void) const { return _ker.value(); }
+ void ker(coco::Object *o) { _ker.value(o); }
+
+ /**
+ * Currently, this "bias" is a Feature object with channel-wise layout
+ *
+ * NOTE This design is subject to change
+ */
+ coco::Object *bias(void) const { return _bias.value(); }
+ void bias(coco::Object *o) { _bias.value(o); }
+
+public:
+ coco::Padding2D *pad(void) { return &_pad; }
+ const coco::Padding2D *pad(void) const { return &_pad; }
+
+ coco::Stride2D *stride(void) { return &_stride; }
+ const coco::Stride2D *stride(void) const { return &_stride; }
+
+private:
+ coco::Def _ofm;
+
+ coco::Use _ifm;
+ coco::Use _ker;
+ coco::Use _bias;
+
+private:
+ coco::Padding2D _pad;
+ coco::Stride2D _stride;
+};
+
+/**
+ * @brief Concatenate feature maps along "depth" dimension through Andoird NN API
+ */
+class ANNDepthConcatF : public coco::Instr,
+ public coco::Object::Producer,
+ public coco::Object::Consumer
+{
+public:
+ ANNDepthConcatF() : _out{this}, _fst{this}, _snd{this}
+ {
+ // DO NOTHING
+ }
+
+public:
+ coco::Instr *loc(void) override { return this; }
+
+public:
+ coco::Object *out(void) const { return _out.value(); }
+ void out(coco::Object *o) { _out.value(o); }
+
+ coco::Object *fst(void) const { return _fst.value(); }
+ void fst(coco::Object *o) { _fst.value(o); }
+
+ coco::Object *snd(void) const { return _snd.value(); }
+ void snd(coco::Object *o) { _snd.value(o); }
+
+private:
+ coco::Def _out;
+
+ // TODO Support variadic-length inputs
+ coco::Use _fst;
+ coco::Use _snd;
+};
+
+#endif // __ENCO_COEX_IR_H__
diff --git a/compiler/enco/core/src/coex/IR.test.cpp b/compiler/enco/core/src/coex/IR.test.cpp
new file mode 100644
index 000000000..e20cbe4fd
--- /dev/null
+++ b/compiler/enco/core/src/coex/IR.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "IR.h"
+
+#include <gtest/gtest.h>
+
+TEST(IRTest, ANNConv2D_default_constructor)
+{
+ ANNConv2D ins;
+
+ ASSERT_EQ(ins.ofm(), nullptr);
+ ASSERT_EQ(ins.ifm(), nullptr);
+ ASSERT_EQ(ins.ker(), nullptr);
+ ASSERT_EQ(ins.bias(), nullptr);
+}
+
+TEST(IRTest, ANNDepthConcatF_default_constructor)
+{
+ ANNDepthConcatF ins;
+
+ ASSERT_EQ(ins.out(), nullptr);
+ ASSERT_EQ(ins.fst(), nullptr);
+ ASSERT_EQ(ins.snd(), nullptr);
+}